13.dp
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
if (n == 0) return 0;
int[] dp = new int[n];
dp[0] = 1;
int res = 1;
for (int i = 1; i < n; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j])
dp[i] = Math.max(dp[i], dp[j] + 1);
}
res = Math.max(res, dp[i]);
}
return res;
}
}
12.
给定 m 个数组,每个数组都已经按照升序排好序了。现在你需要从两个不同的数组中选择两个整数(每个数组选一个)并且计算它们的距离。两个整数 a 和 b 之间的距离定义为它们差的绝对值 |a-b| 。你的任务就是去找到最大距离
示例 1:
输入:
[[1,2,3],
[4,5],
[1,2,3]]
输出: 4
解释:
一种得到答案 4 的方法是从第一个数组或者第三个数组中选择 1,同时从第二个数组中选择 5 。
class Solution {
public int maxDistance(List<List<Integer>> list) {
int res = 0, min = list.get(0).get(0), max = list.get(0).get(list.get(0).size() - 1);
for (int i = 1; i < list.size(); i++) {
res = Math.max(res, Math.max(Math.abs(list.get(i).get(list.get(i).size() - 1)- min),Math.abs(list.get(i).get(0) - max)));
min = Math.min(min, list.get(i).get(0));
max = Math.max(max, list.get(i).get(list.get(i).size() - 1));
}
return res;
}
}
11.贪心
由范围 [1,n] 内所有整数组成的 n 个整数的排列 perm 可以表示为长度为 n - 1 的字符串 s ,其中:
如果 perm[i] < perm[i + 1] ,那么 s[i] == ' i '
如果 perm[i] > perm[i + 1] ,那么 s[i] == 'D' 。
给定一个字符串 s ,重构字典序上最小的排列 perm 并返回它。
示例 1:
输入: s = "I"
输出: [1,2]
解释: [1,2] 是唯一合法的可以生成秘密签名 "I" 的特定串,数字 1 和 2 构成递增关系。
示例 2:
输入: s = "DI"
输出: [2,1,3]
解释: [2,1,3] 和 [3,1,2] 可以生成秘密签名 "DI",
但是由于我们要找字典序最小的排列,因此你需要输出 [2,1,3]。
class Solution {
public int[] findPermutation(String s) {
int[] res = new int[s.length() + 1];
for (int i = 0; i < res.length; i++) {
res[i] = i + 1;
}
int i = 1;
while (i <= s.length()) {
int j = i;
while (i <= s.length() && s.charAt(i - 1) == 'D')
i++;
change(res, j - 1,i);
i++;
}
return res;
}
public void change(int[] a, int start, int end) {
for (int i = 0; i < (end - start) / 2; i++) {
int t = a[start + i];
a[start + i] = a[end - i - 1];
a[end - i - 1] = t;
}
}
}
10.优先队列
给你一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,返回 所需会议室的最小数量 。
示例 1:
输入:intervals = [[0,30],[5,10],[15,20]]
输出:2
示例 2:
输入:intervals = [[7,10],[2,4]]
输出:1
class Solution {
public int minMeetingRooms(int[][] intervals) {
if (intervals == null || intervals.length == 0)
return 0;
int[] start = new int[intervals.length];
int[] end = new int[intervals.length];
for(int i=0;i<intervals.length;i++){
start[i] = intervals[i][0];
end[i] = intervals[i][1];
}
Arrays.sort(start);
Arrays.sort(end);
int rooms = 0, activemetting = 0, i = 0, j = 0;
while (i < intervals.length && j < intervals.length) {
if (start[i] < end[j]) {
activemetting++;
i++;
}else {
activemetting--;
j++;
}
rooms = Math.max(rooms, activemetting);
}
return rooms;
}
}
9.差分
一条街上有很多的路灯,路灯的坐标由数组 lights 的形式给出。 每个 lights[i] = [positioni, rangei] 代表坐标为 positioni 的路灯照亮的范围为 [positioni - rangei, positioni + rangei] (包括顶点)。
位置 p 的亮度由能够照到 p的路灯的数量来决定的。
给出 lights, 返回最亮的位置 。如果有很多,返回坐标最小的。
class Solution {
public int brightestPosition(int[][] lights) {
TreeMap<Integer,Integer> tm = new TreeMap<>();
for(int[] x : lights) {
int left = x[0] - x[1];
// 因为两侧都包含,所以+1
int right = x[0] + x[1] + 1;
tm.put(left,tm.getOrDefault(left,0) + 1);
tm.put(right,tm.getOrDefault(right,0) - 1);
}
int ans = Integer.MIN_VALUE;
// 最大的亮度
int max = 0;
// 当前的亮度
int cur = 0;
for(Map.Entry<Integer,Integer> entry : tm.entrySet()) {
cur += entry.getValue();
if(cur > max) {
max = cur;
ans = entry.getKey();
}
}
return ans;
}
}
8.差分数组
假设你有一个长度为 n 的数组,初始情况下所有的数字均为 0,你将会被给出 k 个更新的操作。
其中,每个操作会被表示为一个三元组:[startIndex, endIndex, inc],你需要将子数组 A[startIndex ... endIndex](包括 startIndex 和 endIndex)增加 inc。
请你返回 k 次操作后的数组。
示例:
输入: length = 5, updates = [[1,3,2],[2,4,3],[0,2,-2]]
输出: [-2,0,3,5,3]
解释:
初始状态:
[0,0,0,0,0]
进行了操作 [1,3,2] 后的状态:
[0,2,2,2,0]
进行了操作 [2,4,3] 后的状态:
[0,2,5,5,3]
进行了操作 [0,2,-2] 后的状态:
[-2,0,3,5,3]
class Solution {
public int[] getModifiedArray(int length, int[][] updates) {
int[] res = new int[length];
if (updates.length == 0) return res;
for (int[] e : updates) {
res[e[0]] += e[2];
if (e[1] < length - 1)
res[e[1] + 1] -= e[2];
}
for (int i = 1; i < length; i++) {
res[i] += res[i - 1];
}
return res;
}
}
7.dp
给定一个 m x n
的二进制矩阵 mat
,返回矩阵中最长的连续1线段。
这条线段可以是水平的、垂直的、对角线的或者反对角线的。
class Solution {
public int longestLine(int[][] M) {
if (M == null || M.length == 0 || M[0].length == 0)
return 0;
int ans = 0;
int[][] horizontal = new int[M.length][M[0].length];
int[][] vertical = new int[M.length][M[0].length];
int[][] diagonal = new int[M.length][M[0].length];
int[][] antidiagonal = new int[M.length][M[0].length];
for (int i = 0; i != M.length; ++i) {
for (int j = 0; j != M[0].length; ++j) {
if (M[i][j] == 0) {
horizontal[i][j] = 0;
vertical[i][j] = 0;
diagonal[i][j] = 0;
antidiagonal[i][j] = 0;
} else {
horizontal[i][j] = j > 0 ? horizontal[i][j - 1] + 1 : 1;
vertical[i][j] = i > 0 ? vertical[i - 1][j] + 1 : 1;
diagonal[i][j] = i > 0 && j > 0 ? diagonal[i - 1][j - 1] + 1 : 1;
antidiagonal[i][j] = i > 0 && j < M[0].length - 1 ? antidiagonal[i - 1][j + 1] + 1 : 1;
ans = Math.max(ans, horizontal[i][j]);
ans = Math.max(ans, vertical[i][j]);
ans = Math.max(ans, diagonal[i][j]);
ans = Math.max(ans, antidiagonal[i][j]);
}
}
}
return ans;
}
}
6.bfs
你被给定一个 m × n 的二维网格 rooms ,网格中有以下三种可能的初始化值:
-1 表示墙或是障碍物
0 表示一扇门
INF 无限表示一个空的房间。然后,我们用 231 - 1 = 2147483647 代表 INF。你可以认为通往门的距离总是小于 2147483647 的。
你要给每个空房间位上填上该房间到 最近门的距离 ,如果无法到达门,则填 INF 即可。
class Solution {
int EMPTY = Integer.MAX_VALUE;
List<int[]> DIRECTIONS = Arrays.asList(
new int[] { 1, 0},
new int[] {-1, 0},
new int[] { 0, 1},
new int[] { 0, -1}
);
public void wallsAndGates(int[][] rooms) {
int m = rooms.length;
if (m == 0) return;
int n = rooms[0].length;
Queue<int[]> q = new LinkedList<>();
for (int row = 0; row < m; row++) {
for (int col = 0; col < n; col++) {
if (rooms[row][col] == 0) {
q.add(new int[] { row, col });
}
}
}
while (!q.isEmpty()) {
int[] point = q.poll();
int row = point[0];
int col = point[1];
for (int[] direction : DIRECTIONS) {
int r = row + direction[0];
int c = col + direction[1];
if (r < 0 || c < 0 || r >= m || c >= n || rooms[r][c] != EMPTY) {
continue;
}
rooms[r][c] = rooms[row][col] + 1;
q.add(new int[] { r, c });
}
}
}
}
5.
由空地(用 0 表示)和墙(用 1 表示)组成的迷宫 maze 中有一个球。球可以途经空地向 上、下、左、右 四个方向滚动,且在遇到墙壁前不会停止滚动。当球停下时,可以选择向下一个方向滚动。
给你一个大小为 m x n 的迷宫 maze ,以及球的初始位置 start 和目的地 destination ,其中 start = [startrow, startcol] 且 destination = [destinationrow, destinationcol] 。请你判断球能否在目的地停下:如果可以,返回 true ;否则,返回 false 。
你可以 假定迷宫的边缘都是墙壁(参考示例)。
class Solution {
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
boolean[][] is = new boolean[maze.length][maze[0].length];
return dfs(is,start,destination,maze);
}
public boolean dfs(boolean[][] is,int[] start, int[] destination,int[][] maze) {
if (is[start[0]][start[1]])
return false;
if (destination[0] == start[0] && destination[1] == start[1])
return true;
is[start[0]][start[1]] = true;
int right = start[1] + 1, left = start[1] - 1, up = start[0] - 1, down = start[0] + 1;
//向右
while (right < maze[0].length && maze[start[0]][right] == 0)
right++;
if (dfs(is,new int[]{start[0],right - 1},destination,maze))
return true;
//向左
while (left >= 0 && maze[start[0]][left] == 0)
left--;
if (dfs(is,new int[]{start[0],left + 1},destination,maze))
return true;
//向上
while (up >= 0 && maze[up][start[1]] == 0)
up--;
if (dfs(is,new int[]{up + 1,start[1]},destination,maze))
return true;
while (down < maze.length && maze[down][start[1]] == 0)
down++;
if (dfs(is,new int[]{down - 1,start[1]},destination,maze))
return true;
return false;
}
}
4.深度优先搜索
给定一个非空 01 二维数组表示的网格,一个岛屿由四连通(上、下、左、右四个方向)的 1 组成,你可以认为网格的四周被海水包围。
请你计算这个网格中共有多少个形状不同的岛屿。两个岛屿被认为是相同的,当且仅当一个岛屿可以通过平移变换(不可以旋转、翻转)和另一个岛屿重合。
示例 1:
11000
11000
00011
00011
给定上图,返回结果 1 。
示例 2:
11011
10000
00001
11011
class Solution {
int path = 0;
public int numDistinctIslands(int[][] grid) {
Set<Integer> set = new HashSet<>();
for (int g = 0; g < grid.length; g++) {
for (int i = 0; i < grid[0].length; i++) {
if (grid[g][i] == 1) {
path = 0;
dfs(grid,g,i,path);
set.add(path);
}
}
}
return set.size();
}
public void dfs(int[][] grid, int i, int j, int pre) {
if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == 0)
return;
grid[i][j] = 0;
path = path * 10 + pre;
dfs(grid, i + 1, j,1);
dfs(grid,i - 1, j, 2);
dfs(grid, i ,j + 1, 3);
dfs(grid,i,j - 1, 4);
path = path * 10 + 5;
}
}
3.深度优先搜索+二分
给定一个 m x n 的整数矩阵 grid,返回从 (0,0) 开始到 (m - 1, n - 1) 在四个基本方向上移动的路径的最大 分数 。
一条路径的 分数 是该路径上的最小值。
例如,路径 8 → 4 → 5 → 9 的得分为 4 。
class Solution {
int n, m;
int[][] dir = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public int maximumMinimumPath(int[][] grid) {
n = grid.length;
m = grid[0].length;
int left = 0, right = Math.min(grid[0][0], grid[n - 1][m - 1]), res = -1;
while (left <= right) {
int mid = (left + right) / 2;
if (dfs(mid, 0, 0, new boolean[n][m], grid)) {
res = Math.max(res, mid);
left = mid + 1;
}else
right = mid - 1;
}
return res;
}
public boolean dfs(int min, int i, int j, boolean[][] is, int[][] grid) {
if (i == n - 1 && j == m - 1)
return true;
is[i][j] = true;
for (int k = 0; k < 4; k++) {
int x = i + dir[k][0];
int y = j + dir[k][1];
if (x >= 0 && y >= 0 && x < n && y < m && !is[x][y] && grid[x][y] >= min) {
if (dfs(min, x, y, is, grid))
return true;
}
}
return false;
}
}
2.dp
假如有一排房子,共 n 个,每个房子可以被粉刷成红色、蓝色或者绿色这三种颜色中的一种,你需要粉刷所有的房子并且使其相邻的两个房子颜色不能相同。
当然,因为市场上不同颜色油漆的价格不同,所以房子粉刷成不同颜色的花费成本也是不同的。每个房子粉刷成不同颜色的花费是以一个 n x 3 的正整数矩阵 costs 来表示的。
例如,costs[0][0] 表示第 0 号房子粉刷成红色的成本花费;costs[1][2] 表示第 1 号房子粉刷成绿色的花费,以此类推。
请计算出粉刷完所有房子最少的花费成本。
示例 1:
输入: costs = [[17,2,17],[16,16,5],[14,3,19]]
输出: 10
解释: 将 0 号房子粉刷成蓝色,1 号房子粉刷成绿色,2 号房子粉刷成蓝色。
最少花费: 2 + 5 + 3 = 10。
示例 2:
输入: costs = [[7,6,2]]
输出: 2
class Solution {
public int minCost(int[][] costs) {
int[][] dp = new int[costs.length][3];
dp[0][0] = costs[0][0];
dp[0][1] = costs[0][1];
dp[0][2] = costs[0][2];
for (int i = 1; i < costs.length; i++) {
dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + costs[i][0];
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + costs[i][1];
dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + costs[i][2];
}
return Math.min(Math.min(dp[costs.length - 1][0], dp[costs.length - 1][1]), dp[costs.length - 1][2]);
}
}
1.dp
有 k 种颜色的涂料和一个包含 n 个栅栏柱的栅栏,请你按下述规则为栅栏设计涂色方案:
每个栅栏柱可以用其中 一种 颜色进行上色。
相邻的栅栏柱 最多连续两个 颜色相同。
给你两个整数 k 和 n ,返回所有有效的涂色 方案数 。
class Solution {
public int numWays(int n, int k) {
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = k;
for (int i = 1; i < n; i++) {
dp[i][0] = dp[i - 1][1];
dp[i][1] = dp[i - 1][0] * (k - 1) + dp[i - 1][1] * (k - 1);
}
return dp[n - 1][0] + dp[n - 1][1];
}
}