从今天开始将自己做过的leetcode进行整理,争取做到bug-free~
根据target求两数在数组中的index
思路:用HashMap(时间复杂度o(n))
public int[] twoSum(int[] numbers, int target) {
int[] res = new int[2];
if (numbers == null || numbers.length < 2) {
return res;
}
HashMap map = new HashMap();
for (int i = 0; i < numbers.length; i++) {
if (map.containsKey(target - numbers[i])) {
res[0] = i;
res[1] = map.get(target - numbers[i]);
return res;
} else {
map.put(numbers[i], i);
}
}
return res;
}
方法2:敏感关键字array和target,想到二分查找法(但需要一点一点挪low和high指针)。
排序后,target == numbers[low]+numbers[high],记录copy[low]和copy[high];target >numbers[low]+numbers[high],说明最大的和最小的加一起还小于target,所以小值要取大一点,即low++;target
给定string(数字组成),输出在键盘上对应的字母组合。
思路:Backtracking
public List letterCombinations(String digits) {
LinkedList ans = new LinkedList();
String[] mapping = new String[] {"0", "1", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
ans.add("");
for(int i =0; i
public List letterCombinations(String digits) {
List res = new ArrayList();
if (digits.isEmpty()) { // 必不可少
return res;
}
res.add("");
String[] s = {" ", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
char[] array = digits.toCharArray();
for (int i = 0; i <= digits.length() - 1; i++) {
int d = array[i] - '0';
List list = new ArrayList();
if (d != 1) {
for (int j = 0; j < res.size(); j++) {
list.add(res.get(j) + s[d].charAt(0));
list.add(res.get(j) + s[d].charAt(1));
list.add(res.get(j) + s[d].charAt(2));
if (d == 7 || d == 9) {
list.add(res.get(j) + s[d].charAt(3));
}
}
}
res = list;
}
return res;
}
public List generateParenthesis(int n) {
ArrayList res = new ArrayList();
if (n <= 0) {
return res;
}
dfs(res, "", n, n);
return res;
}
public void dfs(ArrayList res,String tmp,int left,int right) {
if(left == 0 && right == 0) {
res.add(tmp);
return;
}
if(left > 0) {
dfs(res, tmp + "(", left - 1, right);
}
if(left
public List> solveNQueens(int n) {
List> re = new ArrayList>();
if(n == 1) {
List l = new ArrayList();
l.add("Q");
re.add(l);
return re;
}
int[] Q = new int[n];
DFS(n, Q, re, 0);
return re;
}
public static void DFS(int n, int[] Q, List> re, int row) {
if(row == n) {
List list = new ArrayList();
// put
for(int i = 0; i < n; i++) {
String s = "";
for(int j = 0; j < n; j++) {
if(Q[i] == j) s += "Q";
else s += ".";
}
list.add(s);
}
re.add(list);
} else {
for(int j = 0; j < n; j++) { // 每一列
Q[row] = j;
if(isValid(row, Q))
DFS(n, Q, re, row + 1);
}
}
}
public static boolean isValid(int row, int[] Q) {
for(int i = 0; i < row; i++) {
if(Q[row] == Q[i] || Math.abs(Q[row] - Q[i]) == (row - i))
return false;
}
return true;
}
return the total number of distinct solutions.
和上题一样
public static int count = 0;
public int totalNQueens(int n) {
if(n == 1) return 1;
int[] Q = new int[n];
int re =DFS(n, Q, 0, 0);
return re;
}
public static int DFS(int n, int[] Q, int row, int count) {
if(row == n)
count++;
else {
for(int j = 0; j < n; j++) { // 每一列
Q[row] = j;
if(isValid(row, Q))
count = DFS(n, Q, row + 1, count);
}
}
return count;
}
public static boolean isValid(int row, int[] Q) {
for(int i = 0; i < row; i++) {
if(Q[row] == Q[i] || Math.abs(Q[row] - Q[i]) == (row - i))
return false;
}
return true;
}
Given a 2d grid map of '1'
s (land) and '0'
s (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
11110
11010
11000
00000
Answer: 1
Example 2:
11000
11000
00100
00011
Answer: 3
private int n;
private int m;
public int numIslands(char[][] grid) {
int count = 0;
n = grid.length;
if (n == 0) return 0;
m = grid[0].length;
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++)
if (grid[i][j] == '1') {
DFSMarking(grid, i, j);
++count;
}
}
return count;
}
private void DFSMarking(char[][] grid, int i, int j) {
if (i < 0 || j < 0 || i >= n || j >= m || grid[i][j] != '1') return;
grid[i][j] = '0';
DFSMarking(grid, i + 1, j);
DFSMarking(grid, i - 1, j);
DFSMarking(grid, i, j + 1);
DFSMarking(grid, i, j - 1);
}
思路:1 中心扩展,以每个char为中心找到最大的不重复子集(时间:n*n,不好)
2 map存储遍历的值出现的最后一次位置。若遍历的当前值在map中存在,替换为map中此值位置+1和start的最大值(示例:abba)时间复杂度:o(n)
public int lengthOfLongestSubstring2(String s) {
if (s.length() == 0) return 0;
HashMap map = new HashMap();
int max = 0, start = 0;
for (int i = 0; i < s.length(); ++i) {
if (map.containsKey(s.charAt(i))) {
start = Math.max(start, map.get(s.charAt(i)) + 1);
}
map.put(s.charAt(i), i);
max = Math.max(max, i - start + 1);
}
return max;
}
3 set。两个指针,fast指向j,若not in set中,add;否则用slow除掉start至j的所有值,直到j可以放进来
bug: string为空,s.length()引起NullPointerException
public int lengthOfLongestSubstring(String s) {
int i = 0, j = 0, max = 0;
Set set = new HashSet<>();
while (j < s.length()) {
if (!set.contains(s.charAt(j))) {
set.add(s.charAt(j++));
max = Math.max(max, set.size());
} else {
set.remove(s.charAt(i++));
}
}
return max;
}
public String longestPalindrome(String s) {
if(s.isEmpty()) return "";
if(s.length() == 1) return s;
String longest = "";
for(int i = 0; i < s.length() - 1; i++) {
String s1 = helper(s, i, i);
if(s1.length() > longest.length())
longest = s1;
s1 = helper(s, i, i + 1);
if(s1.length() > longest.length())
longest = s1;
}
return longest;
}
public static String helper(String s, int l, int r) {
while(l >= 0 && r <= s.length() - 1 && s.charAt(l) == s.charAt(r)) {
l--;
r++;
}
return s.substring(l + 1,r);
}
P A H N A P L S I I G Y I R
convert("PAYPALISHIRING", 3)
return
"PAHNAPLSIIGYIR"
public String convert(String s, int numRows) {
if (numRows >= s.length()) {
return s;
}
String[] str = new String[numRows];
for (int i = 0; i < str.length; i++) { // bug point: init(否则为null)
str[i] = "";
}
int i = 0;
while (i < s.length()) {
for (int idx = 0; idx < numRows && i < s.length(); idx++) { // 直下
str[idx] += (s.charAt(i++) + "");
}
for (int j = numRows - 2; j >= 1 && i < s.length(); j--) { //bug:i边界
str[j] += (s.charAt(i++) + "");
}
}
String res = "";
for (String a : str) {
res += a;
}
return res;
}
思路2:依次遍历s,用incre表示下一步要走的方向,index表示下一步要填的位置(好理解)
public String convert3(String s, int numRows) {
if(numRows <= 1) return s;
StringBuilder[] sb=new StringBuilder[numRows];
for(int i = 0; i < sb.length; i++) { // init!
sb[i] = new StringBuilder("");
}
int incre = 1;
int index = 0;
for(int i = 0; i < s.length(); i++) {
sb[index].append(s.charAt(i));
if(index == 0){incre = 1;}
if(index == numRows - 1){incre = -1;}
index += incre;
}
String re = "";
for(int i = 0; i < sb.length; i++) {
re += sb[i];
}
return re.toString();
}
public int myAtoi2(String str) {
if (str.isEmpty()) {
return 0;
}
int base = 0, sign = 1, i = 0;
while (str.charAt(i) == ' ') {
i++;
}
if (str.charAt(i) == '-' || str.charAt(i) == '+') {
sign = (str.charAt(i++) == '-' ? -1 : 1); // 注意符号,没标就是+
}
while (i < str.length() && str.charAt(i) >= '0' && str.charAt(i) <= '9') {
if (base > Integer.MAX_VALUE / 10 || (base == Integer.MAX_VALUE / 10 && str.charAt(i) - '0' > 7)) {
return (sign == 1) ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
base = base * 10 + (str.charAt(i++) - '0'); // 忘了'0'
}
return base * sign;
}
public static String longestCommonPrefix(String[] strs) {
StringBuilder sb = new StringBuilder();
if(strs == null || strs.length == 0)
return sb.toString();
Arrays.sort(strs);
char[] start = strs[0].toCharArray();
char[] end = strs[strs.length - 1].toCharArray();
int j = 0;
int m = Math.min(start.length , end.length);
if(start.length == 0 || end.length == 0){
return sb.toString();
}
while(j < m && start[j] == end[j]){
sb.append(start[j]);
j++;
}
return sb.toString();
}
public String longestCommonPrefix(String[] strs) {
if (strs == null) return null;
if (strs.length == 0) return "";
Arrays.sort(strs);
char[] first = strs[0].toCharArray();
char[] last = strs[strs.length - 1].toCharArray();
int i = 0, len = Math.min(first.length, last.length);
while (i < len && first[i] == last[i]) i++;
return strs[0].substring(0, i);
}
public static String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length < 1) {
return "";
}
for (int idx = 0; idx < strs[0].length(); idx++) {
for (int i = 1; i < strs.length; i++) {
if (strs[i].charAt(idx) != strs[0].charAt(idx)) {
return strs[0].substring(0, idx);
}
}
}
return strs[0];
}
1, 11, 21, 1211, 111221, ...
1
is read off as
"one 1"
or
11
.
11
is read off as
"two 1s"
or
21
.
21
is read off as
"one 2
, then
one 1"
or
1211
.Given an integer
n, generate the
n
th sequence.
public String countAndSay(int n) {
if(n < 1) return null;
String s = "1";
String re = "";
if(n == 1) return s;
re = count(2,n,s + "1");
return re;
}
public static String count(int m,int countnum,String b){
String res = "";
if(m == countnum) {
return b;
}
int tem = 1;
for(int i = 0; i < b.length() - 1; i++) {
if(b.charAt(i) == b.charAt(i + 1)) { // 算有几个重复的
tem++;
}
if(b.charAt(i) != b.charAt(i + 1) || (i + 1) == b.length()-1){
res += Integer.toString(tem);
res += b.charAt(i);
tem = 1;
if(b.charAt(i) != b.charAt(i + 1) && (i + 1) == b.length() - 1) {
res += Integer.toString(tem);
res += b.charAt(i + 1);
}
}
}
return count(++m, countnum, res);
}
Given a string s consists of upper/lower-case alphabets and empty space characters' '
, return the length of last word in the string.
If the last word does not exist, return 0.
public int lengthOfLastWord(String s) {
s = s.trim();
if (s == "") {
return 0;
}
String[] a = s.split(" ");
if (a.length == 1) {
return s.length();
}
return a[a.length - 1].length();
}
public int lengthOfLastWord(String s) {
// trim()去掉字符串首尾的空格
return s.trim().length() - s.trim().lastIndexOf(" ") - 1;
}
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log","cog"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog"
,
return its length 5
.
public int ladderLength(String start, String end, Set dict) {
Set set1 = new HashSet();
Set set2 = new HashSet();
set1.add(start);
set2.add(end);
int min = helper(1, set1, set2, dict);
return min;
}
public static int helper(int num, Set set1, Set set2, Set dict) {
if (set1.isEmpty()) {
return 0;
}
if (set1.size() > set2.size()) {
return helper(num, set2, set1, dict); // 这一步之前忘了加return
}
for (String s : set1) {
dict.remove(s);
}
for (String s : set2) {
dict.remove(s);
}
Set set = new HashSet();
for (String s : set1) {
for (int i = 0; i < s.length(); i++) {
char[] c = s.toCharArray(); // 位置在这里!
for (char a = 'a'; a <= 'z'; a++) {
c[i] = a;
String word = new String(c);
// found the word
if (set2.contains(word)) {
return num + 1;
}
// not
if (dict.contains(word)) {
set.add(word);
}
}
}
}
return helper(num + 1, set2, set, dict);
}
/*
* 方法二:用queue
* 每次访问和str相差一位的所有字符串。visited标志所有访问过的
*/
public int ladderLength(String start, String end, Set dict) {
// Use queue to help BFS
Queue queue = new LinkedList();
queue.add(start);
queue.add(null);
// Mark visited word
Set visited = new HashSet();
visited.add(start);
int level = 1;
while (!queue.isEmpty()) {
String str = queue.poll();
if (str != null) {
// Modify str's each character (so word distance is 1)
for (int i = 0; i < str.length(); i++) {
char[] chars = str.toCharArray();
for (char c = 'a'; c <= 'z'; c++) {
chars[i] = c;
String word = new String(chars);
// Found the end word
if (word.equals(end)) return level + 1;
// Put it to the queue
if (dict.contains(word) && !visited.contains(word)) {
queue.add(word);
visited.add(word);
}
}
}
} else {
level++;
if (!queue.isEmpty()) {
queue.add(null);
}
}
}
return 0;
}
Given s = "the sky is blue
",
return "blue is sky the
".
注意处理时: String[] s1 = s.trim().split(" ");// 两个以上空格,错。"a b"处理后是a, , , ,b
/*
* 最优
* 正则表达式Regex
* "\s" is a regex class for any kind of whitespace (space, tab, newline, etc). Since Java uses "\" as an escape character in strings (e.g. for newlines: "\n"),
* we need to escape the escape character ;-)
* So it becomes "\\s". The "+" means one or more of them.
*/
public String reverseWords2(String s) {
String[] parts = s.trim().split("\\s+"); // trim去掉两边的空格
String out = "";
for (int i = parts.length - 1; i > 0; i--) {
out += parts[i] + " ";
}
return out + parts[0];
}
/*
* 另一种
*/
public String reverseWords3(String s) {
String[] tmp = s.split("\\s");
StringBuilder sb = new StringBuilder();
for(int i = 1; i <= tmp.length; i++){
if(tmp[tmp.length - i].equals("")){
continue;
}
sb.append(tmp[tmp.length - i]);
sb.append(" ");
}
return sb.toString().trim();
}
找两个数组的中位数
思路:分奇偶。奇数是n/2,偶数是n/2 - 1和n/2中值
寻找两个数组中第k大数。等价于寻找并判断两个array中第k/2(从1开始数,即index k/2-1)大的数。
判断两个有序数组A,B中第k大的数,需要判断A[k/2-1]和B[k/2-1]的大小。如果A[k/2-1]==B[k/2-1],那么这个数就是两个数组中第k大的数; 如果A[k/2-1]
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if ((m + n) % 2 == 0) {
double x = (double)findKth(nums1, 0, nums2, 0, (m + n) / 2 + 1); // k传的是第k个,index实则k-1
double y = (double)findKth(nums1, 0, nums2, 0, (m + n) / 2);
return (x + y) / 2;
} else {
return (double)findKth(nums1, 0, nums2, 0, (m + n) / 2 + 1);
}
}
public static double findKth(int[] a, int astart, int[] b, int bstart, int k) {
int m = a.length - astart;
int n = b.length - bstart;
if (m > n) {
return findKth(b, bstart, a, astart, k);
}
if (m == 0) { // 当k = 0,bug
return b[k - 1];
}
if(k == 1)
return Math.min(a[astart], b[bstart]);
int partA = Math.min(k / 2, m);
int partB = k - partA;
if (a[astart + partA - 1] == b[bstart + partB - 1]) {
return a[astart + partA - 1];
} else if (a[astart + partA - 1] < b[bstart + partB - 1]) {
return findKth(a, astart + partA, b, bstart, k - partA);
} else {
return findKth(a, astart,b, bstart + partB, k - partB);
}
}
注意正负,dividend或divisor为-2147483648的情况,要转化为long
另一思路:将dividend和divisor都转为负数(不用转long),再计算
public int divide(int dividend, int divisor) {
if (divisor == 0 || (dividend == Integer.MIN_VALUE && divisor == -1))
return Integer.MAX_VALUE;
if(divisor == 1) return dividend;
boolean isNeg = (divisor < 0 && dividend > 0) || (dividend < 0 && divisor > 0);
int res = 0;
long dvd = Math.abs(dividend);
if(dvd < 0) dvd = -dvd; // Math.abs(Integer.MINVALUE) 不变
long dvs = Math.abs(divisor);
if(dvs < 0) dvs = -dvs;
while(dvd >= dvs) {
long temp = dvs;
long mul = 1;
while(dvd >= (temp << 1)) {
temp <<= 1;
mul <<= 1;
}
dvd -= temp;
res += mul;
}
return isNeg? -res : res;
}
an array sorted in ascending order is rotated at some pivot unknown to you beforehand.find the index of target
思路:需要明确rotated sorted array的特性,那么就是至少有一侧是排好序的.无论pivot在哪
public int search(int[] nums, int target) {
if(nums.length < 1) return -1;
int low = 0;
int high = nums.length - 1;
while(low <= high) {
int mid = (low + high) / 2;
if(nums[mid] < target) {
if(nums[mid] > nums[low]) { // 左侧有序
low = mid + 1;
} else {
if(target > nums[high])
high = mid - 1;
else low = mid + 1;
}
} else if(nums[mid] > target){ // mid大于target
if(nums[mid] < nums[high]) { // 右侧有序
high = mid - 1;
} else {
if(target < nums[low])
low = mid + 1;
else high = mid - 1;
}
} else return mid;
}
return -1;
}
0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).Write a function to determine if a given target is in the array. public boolean search(int[] nums, int target) {
if(nums.length < 1) return false;
int low = 0;
int high = nums.length - 1;
while(low <= high) {
int mid = (low + high) /2;
if(nums[mid] == target) return true;
if(nums[mid] > nums[low]) { // 左侧有序
if (target < nums[mid] && target >= nums[low]) high = mid - 1;
else low = mid + 1;
} else if(nums[mid] < nums[low]){ // 右侧有序
if (target > nums[mid] && target < nums[low]) low = mid + 1;
else high = mid - 1;
} else low++; // mid和low为重复值!
}
return false;
}
public int[] searchRange(int[] nums, int target) {
int[] re = new int[]{-1,-1};
if(nums.length == 0) {
return re;
}
int l = 0, h = nums.length - 1;
while (l < h) {
int mid = (l + h) / 2;
if (nums[mid] < target) {
l = mid + 1;
} else {
h = mid;
}
}
if (nums[l] != target) {
return re;
}
re[0] = l;
h = nums.length - 1;
while (l < h) {
int mid = (l + h) / 2 + 1; // Make mid biased to the right
if (nums[mid] > target) {
h = mid - 1;
} else {
l = mid; // 注意因为上面是 / 2 + 1,所以这里若mid + 1为wrong answer
}
}
re[1] = h;
return re;
}
实现x的n次方
思路:递归调用。考虑n为负的情况
方法二利用比特
public double myPow(double x, int n) {
if (x == 0.0) {
return 0.0;
}
if(n == 0) {
return 1;
}
if(n < 0) {
return 1 / x * myPow(1 / x, -(n + 1)); // 考虑n == Integer.MIN_VALUE的情况
}
if(n == 2) return x * x;
if(n % 2 == 0) return myPow(x * x, n / 2);
else return x * myPow(x * x, n / 2);
}
public double myPow(double x, int n) {
if(n==0) return 1;
if(n < 0) {
n = - n;
x = 1 / x;
}
double ans = 1;
while(n > 0){
if((n & 1) > 0) ans *= x;
x *= x;
n >>= 1;
}
return ans;
}
public static double Pow(double base, int exponent) {
if (base == 0.0 || exponent == 1) {
return base;
}
if (exponent == 0) {
return 1;
}
if (exponent == 1) {
return base;
}
double res = Pow(base, exponent >> 1);
res *= res;
if ((exponent & 1) == 1) { // 奇数与1为1,偶数为0,注意这里:exponent & 1一定要加括号,优先级!
res *= base;
}
return res;
}
思路:x的平方根在1和x之间,利用left和right找到
方法2采用位运算,差不多
public int sqrt(int x) {
if(x == 0) return 0;
int left = 1;
int right = x;
while(true) {
int mid = left + (right - left) / 2;
if(mid > x / mid) right = mid - 1;
else {
if(mid + 1 > x / (mid + 1)) return mid;
left = mid + 1;
}
}
return mid;
}
public int sqrt(int x) {
if(x == 0) return 0;
int h = 0;
while((long)(1< h++;
}
h--;
int b = h - 1; // 从下一位开始
int res = 1 << h;
while(b >= 0) {
if((long)(res | (1 << b)) * (long) (res | (1 << b)) <= x)
res |= (1 << b); // res加上下一位二进制数
b--;
}
return res;
}
二维数组依次递增(行内,行末尾和下一行开头),查询某数
思路:我的想法是二分法找到行,再二分找行内数。最优:既然依次增大, 将二维数组看成一维就可以了
public boolean searchMatrix2(int[][] matrix, int target) {
if (matrix.length < 1 || matrix[0].length < 1) {
return false;
}
int n = matrix.length;
int m = matrix[0].length;
int l = 0, h = m * n - 1;
while (l <= h) {
int mid = (l + h) / 2;
if (matrix[mid / m][mid % m] < target) {
l = mid + 1;
} else if (matrix[mid / m][mid % m] > target){
h = mid - 1;
} else {
return true;
}
}
return false;
}
Two pointers
11. Container With Most Water
题:输入数组表示每条线的高,找到两条线,与x轴组成形状的盛水量最大。
思路:设置两个指针i,j,一头一尾。假设i指向的挡板较低,j较高(height[i] < height[j]),下一步移动哪个指针?
若移动j,无论height[j - 1]是何种高度,形成的面积都小于之前的面积;
若移动i,若height[i+1] <= height[i],面积一定缩小。但若height[i+1] > height[i],面积可能增大。综上,应该移动指向较低挡板的那个指针。
public int maxArea(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int max = 0;
int i = 0, j = height.length-1;
while (i < j) {
max = Math.max(max, (j - i) * Math.min(height[i], height[j]));
if (height[i] < height[j]) { // should move i
int k;
for (k = i + 1; k < j && height[k] <= height[i]; ++k) {}
i = k;
} else { // should move j
int k;
for (k = j - 1; k > i && height[k] <= height[j]; --k) {}
j = k;
}
}
return max;
}
或:
public int maxArea(int[] height) {
if (height.length < 2) return 0;
int ans = 0;
int l = 0;
int r = height.length - 1;
while (l < r) {
ans = Math.max(ans, (r - l) * Math.min(height[l], height[r]));
if (height[l] < height[r]) l++;
else r--;
}
return ans;
}
求a + b + c = 0的所有集合
思路:两个指针代表第2、3个。遍历第一个数时,大于0则不可能。注意去掉duplicate
public List> threeSum(int[] nums) {
List> res = new ArrayList<>();
if(nums.length < 3) return res;
for (int i = 0; i < nums.length - 2; i++) {
if (nums[i] > 0) {
return res;
}
if (i == 0 || nums[i] != nums[i - 1]) {
int l = i + 1;
int h = nums.length - 1;
while (l < h) {
int sum = nums[i] + nums[h] + nums[l];
if (sum == 0) {
res.add(Arrays.asList(nums[i], nums[l], nums[h]));
}
if (sum <= 0) {
while (nums[l] == nums[++l] && l < h); // l < h 容易忘
}
if (sum >= 0) {
while (nums[h] == nums[--h] && l < h);
}
}
}
}
return res;
}
3sum的变体,如果没有3sum==target,要返回最接近target的值。
思路: 需要在使用二分查找法时遍历数组的时候,维护一个最接近target值min。 这道题比3sum和4sum简单的地方就是不需要判断重复问题,因为题目给我们减轻了去重的压力,have exactly one solution。即便要求去重,使用之前说过的两个方法:HashSet和挪动指针法,也可以很容易就去重了。 这里要注意,判断closest的方法是采取target-sum的绝对值与min相比
public int threeSumClosest(int[] nums, int target) {
if(nums == null || nums.length < 3)
return 0;
int res = 0;
int min = Integer.MAX_VALUE;
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
int l = i + 1;
int h = nums.length - 1;
while (l < h) {
int sum = nums[i] + nums[l] + nums[h];
if (target == sum) {
return sum;
} else if (target > sum) {
l++;
} else {
h--;
}
if (Math.abs(target - sum) < min) {
min = Math.abs(target - sum);
res = sum;
}
}
}
return res;
}
思路:遍历固定一位,再调用3sum,注意谢姐
public List> fourSum(int[] nums, int target) {
List> re = new ArrayList>();
Arrays.sort(nums);
if (nums == null || nums.length < 4 || 4 * nums[nums.length - 1] < target) {
return re;
}
for (int i = 0; i < nums.length - 3; i++) {
if(i == 0 || nums[i] != nums[i - 1]) {
if(4 * nums[i] > target) { // to save time
break;
}
re.addAll(sum3(nums, i + 1, target - nums[i]));
}
}
return re;
}
public static List> sum3 (int[] a, int i, int target) {
List> result = new ArrayList>();
for(int j = i; j < a.length - 2; j++) {
if (j == i || a[j] != a[j - 1]){
if (a[j] * 3 > target) { // to save time
break;
}
int l = j + 1;
int h = a.length - 1;
while(l < h) {
int sum = a[j] + a[l] + a[h];
if(sum == target) {
result.add(Arrays.asList(a[i- 1], a[j], a[l], a[h]));
}
if(sum <= target) while(a[l] == a[++l] && l < h);
if(sum >= target) while(a[h] == a[--h] && l < h);
}
}
}
return result;
}
输入nums = [1,1,2],不算重复的,个数为2,且nums前两位要为1,2
最优思路:用一个数id记录当前需要赋值的位置,不用管id之后的。当i不等于i-1,将i赋给id
·public int removeDuplicates2(int[] nums) {
if (nums.length < 2) {
return nums.length;
}
int id = 1;
for(int i = 1; i < nums.length; ++i)
if(nums[i] != nums[i - 1]) {
nums[id++] = nums[i];
}
return id;
}
给定val,remove所有值为val的点,其余点排在前面
思路和#26一样
public int removeElement(int[] nums, int val) {
int id = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) {
nums[id] = nums[i];
id++;
}
}
return id;
}
public int strStr(String haystack, String needle) {
if(haystack == null || needle == null)
return 0;
return haystack.indexOf(needle);
}
不用indexOf()的方法
public int strStr(String haystack, String needle) {
for (int i = 0; ; i++) {
for (int j = 0; ; j++) {
if (j == needle.length()) return i;
if (i + j == haystack.length()) return -1;
if (needle.charAt(j) != haystack.charAt(i + j)) break;
}
}
}
0、1、2代表三种颜色,按顺序在数组中排序(不能用sort function)
两指针
public void sortColors(int[] nums) {
if(nums.length <= 1) return;
int color1 = 0, color3 = nums.length - 1;
for (int i = 0; i <= color3 && color1 < color3; i++) {
if (nums[i] == 0 && i > color1) {
swap(nums, i--, color1++);
} else if (nums[i] == 2 && i < color3) {
swap(nums, i--, color3--);
}
}
}
public void swap(int[] b, int left, int right){
if(left == right) return;
b[left] = b[left] + b[right];
b[right] = b[left] - b[right];
b[left] = b[left] - b[right];
}
"A man, a plan, a canal: Panama" is a palindrome.
"race a car" is not a palindrome.
/*
* 最优
*/
public boolean isPalindrome2(String s) {
if (s.isEmpty()) {
return true;
}
int head = 0, tail = s.length() - 1;
char cHead, cTail;
while(head <= tail) {
cHead = s.charAt(head);
cTail = s.charAt(tail);
if (!Character.isLetterOrDigit(cHead)) {
head++;
} else if(!Character.isLetterOrDigit(cTail)) {
tail--;
} else {
if (Character.toLowerCase(cHead) != Character.toLowerCase(cTail)) {
return false;
}
head++;
tail--;
}
}
return true;
}
/*
* simple,but slow
*/
public boolean isPalindrome3(String s) {
String actual = s.replaceAll("[^A-Za-z0-9]", "").toLowerCase();
String rev = new StringBuffer(actual).reverse().toString();
return actual.equals(rev);
}
考虑reverse后溢出的情况(负数余正数为负)
public int reverse(int x) {
int result = 0;
while(x != 0){
int d = x % 10;
if(Math.abs(result) > Integer.MAX_VALUE / 10) {// 关键
return 0;
}
result = result * 10 + d;
x /= 10;
}
return result;
}
判断回文数
boolean isPalindrome(int x) {
if (x < 0) return false; // 负数没有回文数
int d = 1; //divisor
while (x / d > 10) {
d *= 10;
}
while (x > 0) {
int q = x / d;
int r = x % 10;
if (q != r) {
return false;
}
x = x % d /10; // bug point
d = d / 100;
}
return true;
}
最优解:
public boolean isPalindrome(int x) {
if (x<0 || (x!=0 && x%10==0)) return false;
int rev = 0;
while (x>rev){
rev = rev*10 + x%10;
x = x/10;
System.out.println(rev + " " + x) ;
}
return (x==rev || x==rev/10);
}
"I","V","X","L","C","D","M"
1, 5, 10, 50,100,500,1000
小数在大数右边为加,左边为减(限I,X,C);连写不能超过三个
public String intToRoman(int num) {
String M[] = {"", "M", "MM", "MMM"}; // 1000- 3000
String C[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; // 100-900
String X[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; // 10-90
String I[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; // 1-9
return M[num/1000] + C[(num%1000)/100]+ X[(num%100)/10] + I[num%10];
}
public String intToRoman2(int num) {
int[] values = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
String[] strs = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
StringBuilder sb = new StringBuilder();
for(int i = 0; i < values.length; i++) {
while(num >= values[i]) {
num -= values[i];
sb.append(strs[i]);
}
}
return sb.toString();
}
public int romanToInt(String s) {
if(s == null || s.length() == 0) return 0;
int len = s.length();
HashMap map = new HashMap();
map.put('I',1);
map.put('V',5);
map.put('X',10);
map.put('L',50);
map.put('C',100);
map.put('D',500);
map.put('M',1000);
int result = map.get(s.charAt(len -1));
int pivot = result;
for(int i = len - 2; i>= 0;i--) {
int curr = map.get(s.charAt(i));
if(curr >= pivot){
result += curr;
}else{
result -= curr;
}
pivot = curr;
}
return result;
}
public static int romantoInt(String s) {
int sum = 0;
if (s.contains("IV")) {
sum -= 2;
}
if (s.contains("IX")) {
sum -= 2;
}
if (s.contains("XL")) {
sum -= 20;
}
if (s.contains("XC")) {
sum -= 20;
}
if (s.contains("CD")) {
sum -= 200;
}
if (s.contains("CM")) {
sum -= 200;
}
char[] c = s.toCharArray();
for(int i = 0; i < c.length; i++) {
if (c[i] == 'M') {
sum += 1000;
}
if (c[i] == 'D') {
sum += 500;
}
if (c[i] == 'C') {
sum += 100;
}
if (c[i] == 'L') {
sum += 50;
}
if (c[i] == 'X') {
sum += 10;
}
if (c[i] == 'V') {
sum += 5;
}
if (c[i] == 'I') {
sum += 1;
}
}
}
可以每一位用一个Int表示,存在int[]里面;这个数组最大长度是num1.len +num2.len,比如99 * 99,最大不会超过10000,所以4位就够了。这种个位在后面的,不好做(10的0次方,可惜对应位的数组index不是0而是n-1),所以干脆先把string reverse了代码就清晰好多。注意最后结果前面的0要清掉。
public String multiply(String num1, String num2) {
char[] c1 = new StringBuilder(num1).reverse().toString().toCharArray();
char[] c2 = new StringBuilder(num2).reverse().toString().toCharArray();
int m = c1.length + c2.length;
int[] re = new int[m];
int carry = 0;
for (int i = 0; i < c1.length; i++) {
int a = c1[i] - '0';
for(int j = 0; j < c2.length;j++){
int b = c2[j] - '0';
re[i + j] += a * b; // re[i + j]
}
}
carry = re[0] / 10;
re[0] = re[0] % 10;
StringBuilder sb = new StringBuilder();
sb.append(re[0] + "");
for (int i = 1; i < re.length; i++) {
re[i] = carry + re[i];
carry = re[i] / 10;
sb.insert(0,(re[i] % 10) + ""); // 放在最前
}
while(sb.length() > 0 && sb.charAt(0)=='0') {
sb.deleteCharAt(0);
}
return sb.length() == 0 ? "0" : sb.toString(); // bug :忘记判断0乘以0的情况
}
public String addBinary(String a, String b) {
char[] c1 = new StringBuilder(a).reverse().toString().toCharArray();
char[] c2 = new StringBuilder(b).reverse().toString().toCharArray();
int i = 0;
StringBuilder res = new StringBuilder();
int carry = 0;
while (i < c1.length || i < c2.length) {
int sum = carry;
if (i < c1.length) {
sum += (c1[i] - '0');
}
if (i < c2.length) {
sum += (c2[i] - '0');
}
res.append(sum % 2);
carry = (sum / 2);
i++;
}
if (carry == 1) {
res.append("1");
}
return res.reverse().toString();
}
public String addBinary(String a, String b) {
if(a == null || a.isEmpty())
return b;
if(b == null || b.isEmpty())
return a;
StringBuilder sb = new StringBuilder();
int i = a.length() - 1;
int j = b.length() - 1;
int aBit;
int bBit;
int carry = 0;
int result;
while(i >= 0 || j >= 0 || carry == 1) {
aBit = (i >= 0) ? Character.getNumericValue(a.charAt(i--)) : 0;
bBit = (j >= 0) ? Character.getNumericValue(b.charAt(j--)) : 0;
result = aBit ^ bBit ^ carry;
carry = ((aBit + bBit + carry) >= 2) ? 1 : 0;
sb.append(result);
}
return sb.reverse().toString();
}
1 -> A 2 -> B 3 -> C ... 26 -> Z 27 -> AA 28 -> ABGiven a positive integer, return its corresponding column title as appear in an Excel sheet.
public String convertToTitle(int n) {
return n == 0 ? "" : convertToTitle(--n / 26) + (char)('A' + (n % 26));
}
public String convertToTitle2(int n) {
StringBuilder result = new StringBuilder();
while(n > 0) {
n--;
result.insert(0, (char)('A' + n % 26));
n /= 26;
}
return result.toString();
}
'?' Matches any single character. '*' Matches any sequence of characters (including the empty sequence).意思是任何字符串思路:动态规划。长度为a和b的两个字符串是否匹配,有两种情况:
public static boolean isMatch(String s, String p) {
int m = s.length();
int n = p.length();
boolean[][] dp = new boolean[m + 1][n + 1];
char[] ws = s.toCharArray();
char[] wp = p.toCharArray();
dp[0][0] = true;
for (int i = 1; i <= n; i++) {
dp[0][i] = dp[0][i - 1] && wp[i - 1] == '*';
}
for (int j = 1; j <= m; j++) {
dp[j][0] = false;
}
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (wp[j - 1] == '?' || ws[i - 1] == wp[j - 1])
dp[i][j] = dp[i - 1][j - 1];
else if (wp[j - 1] == '*')
dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
}
}
return dp[m][n];
}
public int maxSubArray(int[] nums) {
if (nums.length < 1) {
return 0;
}
int[] dp = new int[nums.length];
int max = nums[0];
dp[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
if (dp[i - 1] + nums[i] > nums[i]) {
dp[i] = dp[i - 1] + nums[i];
} else {
dp[i] = nums[i];
}
if (dp[i] > max) {
max = dp[i];
}
}
return max;
}
要想起来用dp。当前位置的最大值取决于前一个sum。本题bug free
简单dp,bug-free
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
dp[0][0] = 1;
for (int i = 1; i < n; i++) {
dp[0][i] = 1;
}
for (int i = 1; i < m; i++) {
dp[i][0] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
加入了障碍(1),可以用上面一样的方法,注意去掉0,0处为1的情况
思路2:用一维dp数组。
因为只需要求dp[m-1][n-1],所以先横向再竖向扫描,依次得到每一行的每一列dp[j],然后每行取决于上一行。
每一个点[i,j]都是由每一行的j - 1列相加得到的(依次更新每一行的dp[j])
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
int[][] dp = new int[m][n];
if (obstacleGrid[0][0] == 0) {
return 0;
}
dp[0][0] = 1;
for (int i = 1; i < n && obstacleGrid[0][i] != 1; i++) {
dp[0][i] = 1;
}
for (int i = 1; i < m && obstacleGrid[i][0] != 1; i++) {
dp[i][0] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (obstacleGrid[i][j] == 1) {
dp[i][j] = 0;
} else {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
}
return dp[m - 1][n - 1];
}
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int n = obstacleGrid[0].length;
int[] dp = new int[n];
dp[0] = 1;
for (int[] row : obstacleGrid) {
for (int j = 0; j < n; j++) {
if (row[j] == 1)
dp[j] = 0;
else if (j > 0)
dp[j] += dp[j - 1];
}
}
return dp[n - 1];
}
每个位置都有值,求左上至右下最小的路径值
和63一样,一维dp
public int minPathSum(int[][] grid) {
int n = grid[0].length;
int[] dp = new int[n];
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < n; j++) {
if (i == 0 && j > 0) {
dp[j] = grid[i][j] + dp[j - 1];
} else {
if (j == 0) {
dp[j] += grid[i][0]; // dp[0][0]
} else {
dp[j] = grid[i][j] + Math.min(dp[j], dp[j - 1]);
}
}
}
}
return dp[n - 1];
}
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top(n层)
每一层来自上一层走一步,或者上上层走两步。即dp[i] = dp[i - 1] + dp[i - 2]
public int climbStairs(int n) {
if (n <= 0) {
return 0;
}
if (n <= 2) {
return n;
}
int[] dp = new int[2];
dp[0] = 1;
dp[1] = 2;
for (int i = 2; i < n; i++) {
dp[i % 2] = dp[(i - 1) % 2] + dp[(i - 2) % 2];
}
return dp[(n - 1) % 2];
}
递归也可以做,但是超时(递归的层数太多,需要的内存也指数级递增)
public int climbStairs(int n) {
if (n <= 0) {
return 0;
}
if (n <= 2) {
return n;
}
return climbStairs(n - 1) + climbStairs(n - 2);
}
A message containing letters from A-Z
is being encoded to numbers using the following mapping:
'A' -> 1 'B' -> 2 ... 'Z' -> 26
Given an encoded message containing digits, determine the total number of ways to decode it.For example,Given encoded message "12"
, it could be decoded as "AB"
(1 2) or "L"
(12).The number of ways decoding "12"
is 2.
public int numDecodings(String s) {
if(s == null || s.length() == 0 || s.charAt(0) == '0') {
return 0;
}
int n = s.length();
int[] dp = new int[n + 1];
dp[0] = 1;
dp[1] = s.charAt(0) != '0' ? 1 : 0;
for(int i = 2; i <= n; i++) {
int first = Integer.valueOf(s.substring(i-1, i));
int second = Integer.valueOf(s.substring(i-2, i));
if(first >= 1 && first <= 9) {
dp[i] += dp[i-1];
}
if(second >= 10 && second <= 26) {
dp[i] += dp[i-2];
}
}
return dp[n];
}
120. Triangle
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
题目:三角形第一层至最后一层的最小sum,只能走下一层的相邻点
最优思路(巧妙):一维数组A保存(三角形的高与最大宽相等),从底向上加和
public int minimumTotal2(List> triangle) {
int[] A = new int[triangle.size() + 1];
for(int i = triangle.size() - 1 ; i >= 0; i--) {
for(int j = 0; j < triangle.get(i).size(); j++) {
A[j] = Math.min(A[j], A[j + 1]) + triangle.get(i).get(j);
}
}
return A[0];
}