https://github.com/labuladong/fucking-algorithm
贪心法
力扣55 跳跃游戏
https://leetcode.cn/problems/jump-game/
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int farthest = 0;
for (int i = 0; i < n - 1; i++) {
// 不断计算能跳到的最远距离
farthest = Math.max(farthest, i + nums[i]);
// 可能碰到了0,卡住跳不动了
if (i == farthest) {
return false;
}
}
return farthest >= n - 1;
}
}
力扣45 跳跃游戏II
https://leetcode.cn/problems/jump-game-ii/
class Solution {
public int jump(int[] nums) {
int n = nums.length;
// 跳跃次数
int count = 0;
// 跳跃最远距离
int farthest = 0;
// 记录上次跳跃的最远距离
int current = 0;
for (int i = 0; i < n - 1; i++) {
farthest = Math.max(farthest, i + nums[i]);
if (i == current) {
count++;
current = farthest;
}
}
return count;
}
}
力扣435 无重叠区间
https://leetcode.cn/problems/non-overlapping-intervals/
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
// 按区间结束位置升序排列
Arrays.sort(intervals, new Comparator<int[]>(){
public int compare(int[] a, int[] b){
return a[1] - b[1];
}
});
// 计数有多少个无重叠区间
int count = 1;
int line = intervals[0][1];
for(int[] vals : intervals){
int start = vals[0];
if(start >= line){
count++;
line = vals[1];
}
}
return intervals.length - count;
}
}
力扣452 用最少数量的箭引爆气球
https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/
class Solution {
public int findMinArrowShots(int[][] points) {
// 按区间结束位置升序排列
Arrays.sort(points, (a, b) -> {
// 使用a[1] - b[1]可能会溢出, 有几个用例无法通过
return Integer.compare(a[1], b[1]);
});
// 计数有多少个无重叠区间
int count = 1;
int line = points[0][1];
for(int[] point : points){
int start = point[0];
if(start > line){
count++;
line = point[1];
}
}
return count;
}
}
力扣1024 视频拼接
https://leetcode.cn/problems/video-stitching/
class Solution {
public int videoStitching(int[][] clips, int time) {
// 按起点升序排列,起点相同的降序排列
Arrays.sort(clips, (a, b) -> {
if (a[0] == b[0]) {
return b[1] - a[1];
}
return a[0] - b[0];
});
// 基准线, 可认为时间节点line之前的视频已拼接完成
int line = 0;
int maxEnd = 0;
int count = 0;
int i = 0;
int n = clips.length;
while(i < n && clips[i][0] <= line){
// 寻找开始时间不过线的情况下的最大结束时间
while (i < n && clips[i][0] <= line) {
maxEnd = Math.max(maxEnd, clips[i][1]);
i++;
}
// 更新线
count++;
line = maxEnd;
if (line >= time) {
return count;
}
}
return -1;
}
}
力扣134 加油站
https://leetcode.cn/problems/gas-station/
回溯法
回溯与DFS的区别在于,对多叉树进行遍历时,回溯维护了一条从根结点到当前结点的路径,而DFS通常只关注当前结点。
// 回溯算法标准框架
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
力扣46 全排列
https://leetcode.cn/problems/permutations/
class Solution {
List<List<Integer>> res;
/**
* 主方法
*/
public List<List<Integer>> permute(int[] nums) {
res = new LinkedList();
List<Integer> track = new LinkedList();
boolean[] used = new boolean[nums.length];
backtrack(nums, track, used);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int[] nums, List<Integer> track, boolean[] used){
// 结束条件: nums中的元素全部在track中出现
if(track.size() == nums.length){
// 这里之所以要new LinkedList, 是因为回溯时用的始终是同一个list
res.add(new LinkedList(track));
return;
}
for(int i = 0; i < nums.length; i++){
// 做选择
if(used[i]){
continue;
}
track.add(nums[i]);
used[i] = true;
// 回溯
backtrack(nums, track, used);
// 撤销选择
track.remove(track.size() - 1);
used[i] = false;
}
}
}
力扣78 子集
https://leetcode.cn/problems/subsets/
class Solution {
List<List<Integer>> res;
List<Integer> track;
/**
* 主方法
*/
public List<List<Integer>> subsets(int[] nums) {
res = new LinkedList();
track = new LinkedList();
backtrack(nums, 0);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int[] nums, int start){
// 前序位置
res.add(new LinkedList(track));
// 回溯
for(int i = start; i < nums.length; i++){
track.add(nums[i]);
backtrack(nums, i + 1);
track.remove(track.size() - 1);
}
}
}
力扣77 组合
https://leetcode.cn/problems/combinations/
class Solution {
List<List<Integer>> res;
List<Integer> track;
/**
* 主方法
*/
public List<List<Integer>> combine(int n, int k) {
if(k > n){
return null;
}
res = new LinkedList();
track = new LinkedList();
backtrack(n, k, 1);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int n, int k, int start){
if(track.size() == k){
res.add(new LinkedList(track));
return;
}
for(int i = start; i <= n; i++){
track.add(i);
backtrack(n, k, i + 1);
track.remove(track.size() - 1);
}
}
}
力扣90 子集II
https://leetcode.cn/problems/subsets-ii/
class Solution {
List<List<Integer>> res;
List<Integer> track;
/**
* 主方法
*/
public List<List<Integer>> subsetsWithDup(int[] nums) {
res = new LinkedList();
track = new LinkedList();
Arrays.sort(nums);
backtrack(nums, 0);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int[] nums, int start){
res.add(new LinkedList(track));
for(int i = start; i < nums.length; i++){
if(i > start && nums[i] == nums[i - 1]){
continue;
}
track.add(nums[i]);
backtrack(nums, i + 1);
track.remove(track.size() - 1);
}
}
}
力扣47 全排列II
https://leetcode.cn/problems/permutations-ii/
class Solution {
List<List<Integer>> res = new LinkedList();
List<Integer> track = new LinkedList();
boolean[] used;
/**
* 主方法
*/
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
used = new boolean[nums.length];
backtrack(nums, track, used);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int[] nums, List<Integer> track, boolean[] used){
if(track.size() == nums.length){
res.add(new LinkedList(track));
return;
}
for(int i = 0; i < nums.length; i++){
if(used[i]){
continue;
}
// !used[i - 1]保证重复数字之间的顺序恒定
if(i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){
continue;
}
track.add(nums[i]);
used[i] = true;
backtrack(nums, track, used);
track.remove(track.size() - 1);
used[i] = false;
}
}
}
力扣39 组合总和
https://leetcode.cn/problems/combination-sum/
class Solution {
List<List<Integer>> res = new LinkedList();
List<Integer> track = new LinkedList();
int sum = 0;
/**
* 主方法
*/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backtrack(candidates, target, 0);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int[] candidates, int target, int start){
if(sum == target){
res.add(new LinkedList(track));
return;
}
if(sum > target){
return;
}
for(int i = start; i < candidates.length; i++){
sum += candidates[i];
track.add(candidates[i]);
backtrack(candidates, target, i);
sum -= candidates[i];
track.remove(track.size() - 1);
}
}
}
力扣22 括号生成
https://leetcode.cn/problems/generate-parentheses/
class Solution {
List<String> res = new LinkedList();
StringBuilder track = new StringBuilder();
/**
* 主方法
*/
public List<String> generateParenthesis(int n) {
backtrack(n, 0, 0);
return res;
}
/**
* 回溯方法
*/
public void backtrack(int n, int left, int right){
if(left > n || right > n){
return;
}
if(left < right){
return;
}
if(left == n && right == n){
res.add(track.toString());
}
// 因为选择只有'('和')'两种, 所以没有写for循环
track.append('(');
backtrack(n, left + 1, right);
track.deleteCharAt(track.length() - 1);
track.append(')');
backtrack(n, left, right + 1);
track.deleteCharAt(track.length() - 1);
}
}
力扣51 N皇后
https://leetcode.cn/problems/n-queens/
力扣698 划分为k个相等的子集
https://leetcode.cn/problems/partition-to-k-equal-sum-subsets/
DFS:深度优先搜索
// 二维矩阵遍历框架
void dfs(int[][] grid, int i, int j, boolean[][] visited) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
// 超出索引边界
return;
}
if (visited[i][j]) {
// 已遍历过(i, j)
return;
}
// 进入节点(i, j)
visited[i][j] = true;
dfs(grid, i - 1, j, visited); // 上
dfs(grid, i + 1, j, visited); // 下
dfs(grid, i, j - 1, visited); // 左
dfs(grid, i, j + 1, visited); // 右
}
力扣200 岛屿数量
https://leetcode.cn/problems/number-of-islands/
class Solution {
int m;
int n;
/**
* 主方法
*/
public int numIslands(char[][] grid) {
m = grid.length;
n = grid[0].length;
// 计算岛屿的数量
int res = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == '1'){
res++;
dfs(grid, i, j);
}
}
}
return res;
}
/**
* 深度优先搜索, 每找到一个岛屿, 就把这个岛屿沉入水底
*/
public void dfs(char[][] grid, int i, int j){
if(i < 0 || i >= m || j < 0 || j >= n){
return;
}
if(grid[i][j] == '0'){
return;
}
grid[i][j] = '0';
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
}
}
力扣1254 统计封闭岛屿的数目
https://leetcode.cn/problems/number-of-closed-islands/
class Solution {
int m;
int n;
/**
* 主方法
*/
public int closedIsland(int[][] grid) {
m = grid.length;
n = grid[0].length;
// 先把靠近四条边的土地沉入水底
for(int i = 0; i < m; i++){
dfs(grid, i, 0);
dfs(grid, i, n - 1);
}
for(int j = 0; j < n; j++){
dfs(grid, 0, j);
dfs(grid, m - 1, j);
}
// 统计封闭岛屿数目
int res = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 0){
res++;
dfs(grid, i, j);
}
}
}
return res;
}
/**
* 深度优先搜索, 每找到一个岛屿, 就把这个岛屿沉入水底
*/
public void dfs(int[][] grid, int i, int j){
if(i < 0 || i >= m || j < 0 || j >= n){
return;
}
if(grid[i][j] == 1){
return;
}
grid[i][j] = 1;
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
}
}
力扣1020 飞地的数量
https://leetcode.cn/problems/number-of-enclaves/
class Solution {
int m;
int n;
/**
* 主方法
*/
public int numEnclaves(int[][] grid) {
m = grid.length;
n = grid[0].length;
// 先把靠近四条边的土地沉入水底
for(int i = 0; i < m; i++){
dfs(grid, i, 0);
dfs(grid, i, n - 1);
}
for(int j = 0; j < n; j++){
dfs(grid, 0, j);
dfs(grid, m - 1, j);
}
// 统计飞地的数量
int res = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 1){
res++;
}
}
}
return res;
}
/**
* 深度优先搜索
*/
public void dfs(int[][] grid, int i, int j){
if(i < 0 || i >= m || j < 0 || j >= n){
return;
}
if(grid[i][j] == 0){
return;
}
grid[i][j] = 0;
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
}
}
力扣695 岛屿的最大面积
https://leetcode.cn/problems/max-area-of-island/
class Solution {
int m;
int n;
/**
* 主方法
*/
public int maxAreaOfIsland(int[][] grid) {
m = grid.length;
n = grid[0].length;
// 计算岛屿的最大面积
int maxArea = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 1){
maxArea = Math.max(dfs(grid, i, j), maxArea);
}
}
}
return maxArea;
}
/**
* 深度优先搜索, 获取当前岛屿的面积
*/
public int dfs(int[][] grid, int i, int j){
if(i < 0 || i >= m || j < 0 || j >= n){
return 0;
}
if(grid[i][j] == 0){
return 0;
}
grid[i][j] = 0;
return dfs(grid, i - 1, j) + dfs(grid, i, j - 1) + dfs(grid, i + 1, j) + dfs(grid, i, j + 1) + 1;
}
}
力扣1905 统计子岛屿
https://leetcode.cn/problems/count-sub-islands/
class Solution {
int m;
int n;
/**
* 主方法
*/
public int countSubIslands(int[][] grid1, int[][] grid2) {
m = grid1.length;
n = grid1[0].length;
// 去掉grid2中不是子岛屿的岛屿
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid1[i][j] == 0 && grid2[i][j] == 1) {
dfs(grid2, i, j);
}
}
}
// 统计子岛屿
int res = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid2[i][j] == 1){
res++;
dfs(grid2, i, j);
}
}
}
return res;
}
/**
* 深度优先搜索, 每找到一个岛屿, 就把这个岛屿沉入水底
*/
public void dfs(int[][] grid2, int i, int j){
if(i < 0 || i >= m || j < 0 || j >= n){
return;
}
if(grid2[i][j] == 0){
return;
}
grid2[i][j] = 0;
dfs(grid2, i - 1, j);
dfs(grid2, i, j - 1);
dfs(grid2, i + 1, j);
dfs(grid2, i, j + 1);
}
}
力扣37 解数独
https://leetcode.cn/problems/sudoku-solver/
BFS:广度优先搜索
// 计算从起点start到终点target的最近距离
int BFS(Node start, Node target) {
// 核心数据结构: 队列
Queue<Node> q;
// 已经经过的结点
Set<Node> visited;
// 将起点加入队列
q.offer(start);
visited.add(start);
// 记录扩散的步数
int step = 0;
while (q not empty) {
int sz = q.size();
// 将当前队列中的所有节点向四周扩散
for (int i = 0; i < sz; i++) {
Node cur = q.poll();
// 划重点:这里判断是否到达终点
if (cur is target)
return step;
// 将cur的相邻节点加入队列
for (Node x : cur.adj()) {
if (x not in visited) {
q.offer(x);
visited.add(x);
}
}
}
// 划重点:更新步数在这里
step++;
}
}
力扣111 二叉树的最小深度
https://leetcode.cn/problems/minimum-depth-of-binary-tree/
class Solution {
public int minDepth(TreeNode root) {
if(root == null){
return 0;
}
// bfs核心数据结构: 队列
Queue<TreeNode> queue = new LinkedList();
queue.offer(root);
int depth = 0;
while(!queue.isEmpty()){
depth++;
// 逐层for循环
int sz = queue.size();
// 这里有个小坑, 在循环体中queue.size()是会变化的, 所以循环判定条件使用 i < sz 而不是 i < queue.size()
for(int i = 0; i < sz; i++){
TreeNode current = queue.poll();
if(current.left == null && current.right == null){
return depth;
}
if(current.left != null){
queue.offer(current.left);
}
if(current.right != null){
queue.offer(current.right);
}
}
}
return depth;
}
}
力扣752 打开转盘锁
https://leetcode.cn/problems/open-the-lock/
力扣773 滑动谜题
https://leetcode.cn/problems/sliding-puzzle/
分治法
力扣241 为运算表达式设计优先级
https://leetcode.cn/problems/different-ways-to-add-parentheses/
class Solution {
public List<Integer> diffWaysToCompute(String expression) {
List<Integer> res = new LinkedList();
for(int i = 0; i < expression.length(); i++){
char c = expression.charAt(i);
if(c == '+' || c == '-' || c == '*'){
// 分
List<Integer> left = diffWaysToCompute(expression.substring(0, i));
List<Integer> right = diffWaysToCompute(expression.substring(i + 1));
// 治
for(int a : left){
for(int b : right){
if(c == '+'){
res.add(a + b);
}
if(c == '-'){
res.add(a - b);
}
if(c == '*'){
res.add(a * b);
}
}
}
}
}
// 纯数字
if (res.isEmpty()) {
res.add(Integer.parseInt(expression));
}
return res;
}
}
位运算
// 将英文字符转换成小写
('a' | ' ') => 'a'
('A' | ' ') => 'a'
// 将英文字符转换成大写
('b' & '_') => 'B'
('B' & '_') => 'B'
// 将英文字符大小写互换
('d' ^ ' ') => 'D'
('D' ^ ' ') => 'd'
// 判断两个整数是否异号
int a = -1, b = 2;
boolean m = ((a ^ b) < 0); // true
int c = 3, d = 2;
boolean n = ((c ^ d) < 0); // false
// 一个数和它本身做异或运算结果为0
a ^ a = 0;
// 一个数和0做异或运算的结果为它本身
a ^ 0 = a;
力扣191 位1的个数
https://leetcode.cn/problems/number-of-1-bits/
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int res = 0;
while(n != 0){
// 经典 n & (n - 1), 抹去最低的一位1
n = n & (n - 1);
res++;
}
return res;
}
}
力扣231 2的幂
https://leetcode.cn/problems/power-of-two/
class Solution {
public boolean isPowerOfTwo(int n) {
if (n <= 0){
return false;
}
// 2的幂的各个位置上有且仅有一个1
return (n & (n - 1)) == 0;
}
}
力扣136 只出现一次的数字
https://leetcode.cn/problems/single-number/
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for(int i : nums){
// 异或的性质: a ^ 0 = a, a ^ a = 0
res ^= i;
}
return res;
}
}
力扣268 丢失的数字
https://leetcode.cn/problems/missing-number/
class Solution {
public int missingNumber(int[] nums) {
int res = 0;
// a ^ 0 = a
for(int i = 0; i <= nums.length; i++){
res ^= i;
}
// a ^ a = 0
for(int j : nums){
res ^= j;
}
return res;
}
}