赞
踩
LeetCode总结
所有的题目总结均在每一个package的README中
目录
搜索(回溯、BFS、DFS):
回溯:数独、N皇后、37、51、79、93、[212、301]
BFS:矩阵、单词变换
排列、组合、分割、子集:四大类问题,常用回溯、DFS解决
图的搜索:DFS、BFS、并查集、Flood
并查集(TODO)
二分查找:
g 函数,利用边界
K th 问题
旋转数组
双指针:
左右指针:数组(或字符串)问题,二分查找也算是双指针,三数之和,Sunday算法
快慢指针:链表中环的问题
滑动窗口:更新窗口
链表:
链表的基本操作
旋转(K组旋转,奇偶旋转)、拆分
归并
判断环(快慢指针)
二叉树:
遍历
深度、层次
树的结构相关
树的路径相关,递归的过程之中不断更新全局变量
剪枝
二叉搜索树
数学:
概率:洗牌算法、蓄水池抽样、蒙特卡洛
数论:素数,最小公倍数,最大公约数
位运算:异或,与的巧妙用法
特殊的数:有效数字(状态机),第n个丑数,平方数(DP解法),回文数
数字的转化:溢出检测、模拟运算(时刻注意溢出)、罗马、字符转成数字;分数转小数
其他:Pow()快速幂算法,众数投票法
动态规划
排序
数据结构
单调栈
单调队列
图
分治
贪心
心法宝典
递归要素:开头-判断边界(退出条件);中间-进行相关的计算、缩小范围递归(经常用到全局变量哦);结尾-返回值(还要学会如何利用返回值)
反转链表:迭代写法返回prev,递归写法每次考虑两个节点(返回值是前递归的返回值)
采样:n个数随机采样m个,knuth采样:对于每个下标[0,n) 每次n--, 若rand()%n < m时才m--
Shuffle:knuth Shuffle,i从后往前,每次从[0,i]选择一个位置与i交换
DP: DP 就是填表法,首先明确初始值(并且要明确这个值是什么值),然后要明确填表的顺序,最后是如何填(状态转移方程)。
使用移位运算一定要注意运算符的优先级
双指针
双指针主要分为三类:左右(一般是数组或者字符串,经典的有三数之和和四数之和),快慢指针(一般是和链表环有关系),滑动窗口
1. 序列
无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。注意区分子串与子序列区别
// [i, j), 无重复的时候j++, 有重复的时候i++
public int lengthOfLongestSubstring(String s) {
Set set = new HashSet<>(); // int[] dic = new int[256];
int i = 0, j = 0;
int ret = 0;
while (j < s.length() && i <= j) {
// 是否存在重复字符
if (!set.contains(s.charAt(j))) {
set.add(s.charAt(j));
j++;
ret = Math.max(ret, j-i); // 注意此处没+1,因为j先自增了
} else {
set.remove(s.charAt(i));
i++;
}
}
return ret;
}
盛最多水的容器
不能倾斜容器,且 n (数组的长度)的值至少为 2。
思路:我们要做的就是保证宽度最大的情况下,高度最大; 一开始宽度最大,然后逐步减少宽度;这个时候要不断的去更新高度,使得高度尽量的大,如何移动较大的一端,那么面积肯定是减小的;移动较小的那一个端,面积有可能增大
public int maxArea(int[] height) {
int i = 0, j = height.length-1;
int maxValue = 0;
while (j - i >= 1) {
System.out.println((j-i) * Math.min(height[i], height[j]));
maxValue = Math.max(maxValue, (j-i) * Math.min(height[i], height[j]));
if (height[i] < height[j]) {
i++;
} else {
j--;
}
}
return maxValue;
}
三数之和
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a, b, c, 使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
// 难点: 三数之和 -> 两数之和(哈希、排序+双指针); 排除重复
// 排序+双指针 : valtarget时 high--; val==target时low++ & high--
// 如何跳过重复
public List> threeSum(int[] nums) {
List> ret = new LinkedList<>();
Integer iPre = null;
Arrays.sort(nums);
for (int i = 0; i < nums.length-2; i++) {
if (nums[i] > 0) break;
int j = i+1, k = nums.length-1, sum = 0 - nums[i];
Integer jPre = null, kPre = null;
if (iPre != null && iPre == nums[i]) continue; // 去重
iPre = nums[i];
while (j < k) {
if (nums[j] + nums[k] < sum) {
j++;
}
else if (nums[j] + nums[k] > sum) {
k--;
} else {
if (jPre == null || (jPre != nums[j] && kPre != nums[k])) {
ret.add(Arrays.asList(nums[i], nums[j], nums[k]));
}
jPre = nums[j];
kPre = nums[k];
j++;
k--;
}
}
}
return ret;
}
最接近的三数之和
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
// 本题假定只有 一个答案,所以不用去重了
// 和第15题思路一模一样
public int threeSumClosest(int[] nums, int target) {
// nums.length > 3
Arrays.sort(nums);
int ret = nums[0]+nums[1]+nums[2];
for (int i = 0; i < nums.length-2; i++) {
int j = i+1, k = nums.length-1, sum;
while (j < k) {
sum = nums[i] + nums[j] + nums[k];
if (Math.abs(sum-target) < Math.abs(ret-target)) {
ret = sum;
}
if (sum == target) {
return target;
} else if (sum > target) {
k--;
} else {
j++;
}
}
}
return ret;
}
四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d , 使得 a + b + c + d 的值与 target 相等? 找出所有满足条件且不重复的四元组。和第15题一个思路。
// 双指针 + 去重
public List> fourSum(int[] nums, int target) {
List> ret = new LinkedList<>();
Integer aPre = null;
Arrays.sort(nums);
for (int a = 0; a < nums.length-3; a++) {
if (nums[a] > 0 && nums[a] > target) break; // 注意一定要加nums[a] > 0
if (aPre != null && aPre == nums[a]) continue;
aPre = nums[a];
Integer bPre = null;
for (int b = a+1; b < nums.length-2; b++) {
if (nums[b] > 0 && nums[a] + nums[b] > target) break;
if (bPre != null && bPre == nums[b]) continue; // 去重
bPre = nums[b];
int c = b+1, d = nums.length-1, sum = target - (nums[a] + nums[b]);
Integer cPre = null, dPre = null;
while (c < d) {
if (nums[c] + nums[d] < sum) c++;
else if (nums[c] + nums[d] > sum) d--;
else {
if (cPre == null || (cPre != nums[c] && dPre != nums[d])) {
ret.add(Arrays.asList(nums[a], nums[b], nums[c], nums[d]));
}
cPre = nums[c];
dPre = nums[d];
c++;
d--;
}
}
}
}
return ret;
}
删除排序数组的重复项
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。不需要考虑数组中超出新长度后面的元素。
public int removeDuplicates(int[] nums) {
int i = 0, len = nums.length;
for (int j = 0; j < len; j++) {
if (j+1 < len && nums[j] != nums[j+1]) {
nums[i++] = nums[j];
}
}
if (len > 0) nums[i++] = nums[len-1];
return i;
}
移除元素
原地移除元素,分很多种情况。
// 2. 当要移动的很多时候
public int removeElement(int[] nums, int val) {
if (nums == null || nums.length == 0) return 0;
int i = 0, j = nums.length-1;
while (i <= j) {
while (i <= j && nums[i] != val) i++;
while (i <= j && nums[j] == val) j--;
if (j > i) nums[i++] = nums[j--];
}
// if (nums[i] == val) return i;
return j+1;
}
// 2.2 只和最后一个元素交换
public int removeElement2(int[] nums, int val) {
if (nums == null || nums.length == 0) return 0;
int i = 0, n = nums.length;
while (i < n) {
if (nums[i] == val) {
nums[i] = nums[n-1];
n--;
} else {
i++;
}
}
// if (nums[i] == val) return i;
return n;
}
模式匹配
不要求掌握较难的KMP算法,掌握Sunday算法。
// Sunday算法:如果当前窗口不匹配,比较窗口下一个字符串;下一个字符串在shift数组中的位置,就是窗口要偏移的距离
// 先计算shift数组 every char : len(needle) - max(char) otherwise: len+1
public int strStr(String haystack, String needle) {
// 使用 HashMap 实现shift数组
HashMap shiftMap = new HashMap<>();
int len = needle.length();
for (int i = 0; i < len; i++) {
Character character = needle.charAt(i);
if (!shiftMap.containsKey(character)) {
for (int j = len-1; j >= 0; j--) {
if (needle.charAt(j) == character) {
shiftMap.put(character, len-j);
break;
}
}
}
}
int p = 0;
while (p + len <= haystack.length()) {
int i;
for (i = 0; i < len; i++) {
if (haystack.charAt(p+i) != needle.charAt(i)) break;
}
if (i == len) return p;
else if (p+len == haystack.length()) return -1;
else p += shiftMap.getOrDefault(haystack.charAt(p+len), len+1);
}
return -1;
}
颜色分类
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。使用一趟扫描。
private void swap(int[] nums, int i, int j) {
if (nums[i] == nums[j]) return;
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
public void sortColors(int[] nums) {
int p0 = 0, p2 = nums.length-1;
int cur = 0;
while (cur <= p2) {
if (nums[cur] == 0) swap(nums, cur++, p0++);
else if (nums[cur] == 2) swap(nums, cur, p2--);
else cur++;
}
}
合并两个有序数组
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。初始化 nums1 和 nums2 的元素数量分别为 m 和 n。你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m-1, j = n-1, k = m+n-1;
while (i >= 0 && j >= 0) {
if (nums1[i] < nums2[j]) {
nums1[k--] = nums2[j--];
} else {
nums1[k--] = nums1[i--];
}
}
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}
搜索二维矩阵 II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。
该矩阵具有以下特性:每行的元素从左到右升序排列。每列的元素从上到下升序排列。
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix.length == 0 || matrix[0].length == 0) return false;
int m = matrix.length, n = matrix[0
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。