「滑动窗口」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
//
// 注意:
// 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
// 如果 s 中存在这样的子串,我们保证它是唯一的答案。
//
// 示例 1:
//输入:s = "ADOBECODEBANC", t = "ABC"
//输出:"BANC"
//
//
// 示例 2:
//输入:s = "a", t = "a"
//输出:"a"
//
//
// 示例 3:
//输入: s = "a", t = "aa"
//输出: ""
//解释: t 中两个字符 'a' 均应包含在 s 的子串中,
//因此没有符合条件的子字符串,返回空字符串。
//
//
// 提示:
// 1 <= s.length, t.length <= 10⁵
// s 和 t 由英文字母组成
//进阶:你能设计一个在 o(n) 时间内解决此问题的算法吗? Related Topics 哈希表 字符串 滑动窗口 👍 1527 👎 0


import java.util.HashMap;
import java.util.Map;

class Solution
{
public String minWindow(String s, String t) {
// 存放t字符的数量,方便进行比对
Map<Character, Integer> need = new HashMap<>();
// 存放由截取的s字符数量
Map<Character, Integer> window = new HashMap<>();
for (char charArray : t.toCharArray()) {
need.put(charArray, need.getOrDefault(charArray, 0) + 1);
}
int left = 0, start = 0, valid = 0, len = Integer.MAX_VALUE;
for (int right = 0; right < s.length(); right++) {
char RChar = s.charAt(right);
// need如果存在右指针所指字符,window将添加所指字符数量
if (need.containsKey(RChar)) {
window.put(RChar, window.getOrDefault(RChar, 0) + 1);
// 当need的右指针所指字符数量与window的右指针所指字符数量相等时,对total数量加一
if (window.get(RChar).equals(need.get(RChar))) {
valid++;
}
}
// 当valid达到need字符种类数时寻找最小值
while (valid == need.size()) {
if (right - left + 1 < len) {
len = right - left + 1;
// 记录最小值一开始的位置
start = left;
}
// 获取左指针当前字符,并将左指针左移
char LChar = s.charAt(left++);
if (window.containsKey(LChar)) {
// 当LChar的个数相等时,说明左指针左移一位后肯定不相等,所以total减一
if (window.get(LChar).equals(need.get(LChar))) {
valid--;
}
window.put(LChar, window.get(LChar) - 1);
}
}
}
return len == Integer.MAX_VALUE ? "" : s.substring(start, start + len);
}
}

前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和

prefix[i]就代表着nums[0..i-1]所有元素的累加和,如果我们想求区间nums[i..j]的累加和,只要计算prefix[j+1] - prefix[i]即可,而不需要遍历整个区间求和。

「差分数组」

Read more »

图片

根据邻接表建图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<Integer>[] buildGraph(int numCourses, int[][] prerequisites) {
// 图中共有 numCourses 个节点
List<Integer>[] graph = new LinkedList[numCourses];
for (int i = 0; i < numCourses; i++) {
graph[i] = new LinkedList<>();
}
for (int[] edge : prerequisites) {
int from = edge[1];
int to = edge[0];
// 修完课程 from 才能修课程 to
// 在图中添加一条从 from 指向 to 的有向边
graph[from].add(to);
}
return graph;
}
Read more »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution
{
// 面积
//矩形的长度:两条垂直线的距离
//矩形的宽度:两条垂直线其中较短一条的长度
public int maxArea(int[] height) {
//设置两个指针 left 和 right,分别指向数组的最左端和最右端。 此时,两条垂直线的距离是最远的,
int l = 0, r = height.length - 1;
int res = 0;
while (l < r) {
// 若要下一个矩形面积比当前面积来得大,
int lHigh = height[l];
int rHigh = height[r];
int area = Math.min(lHigh, rHigh) * (r - l);
res = Math.max(res, area);
// 把 height[left] 和 height[right] 中较短的垂直线往中间移动,
// 看看是否可以找到更长的垂直线。
if (lHigh <= rHigh) {++l;
} else {--r;}
//例子的核心在于:贪心,每次保留最长的边向中间求面积
}
return res;
}
}

1
2
3
4
5
6
7
8
9
10
11
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?

//输入:m = 3, n = 2
//输出:3
//解释:
//从左上角开始,总共有 3 条路径可以到达右下角。
//1. 向右 -> 向下 -> 向下
//2. 向下 -> 向下 -> 向右
//3. 向下 -> 向右 -> 向下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
//region 初始化:一直往右边或一直往下面走
for (int i = 0; i < m; i++) {
dp[i][0] = 1;
}
for (int i = 0; i < n; i++) {
dp[0][i] = 1;
}
//endregion
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
// dp[i - 1][j] + dp[i][j - 1] 分别表示从 左边和上面 过来
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}

public int uniquePaths2(int m, int n) {
int[] pre = new int[n];
int[] cur = new int[n];
Arrays.fill(pre, 1);
Arrays.fill(cur, 1);
//滚动数组,只要上一行的数据
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
// cur[j-1]表示从左边过来
// pre[j] 表示从上面过来
cur[j] = cur[j - 1] + pre[j];
}
pre = cur.clone();
}
return pre[n - 1];
}

public int uniquePaths3(int m, int n) {
int[] dp = new int[n];
Arrays.fill(dp, 1);
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
//dp[j]是上一次计算后的结果再加上左边的 dp[j-1]即为当前结果
dp[j] += dp[j - 1];
}
}
return dp[n - 1];
}

按权值随机选择算法

给你一个 下标从 0 开始 的正整数数组 w ,其中 w[i] 代表第 i 个下标的权重。

请你实现一个函数 pickIndex ,它可以 随机地 从范围 [0, w.length - 1] 内(含 0 和 w.length - 1)选出并返回一个下标。选取下标 i 的 概率 为 w[i] / sum(w) 。

Read more »

假设节点为[a,b,c,d],在邻接矩阵中,

  • 无向图:如果v1到v2有边,则邻接矩阵M[v_1][v_2]=M[v_2][v_1]=1,否则=0.

    M[0]中的值为1的元素的个数为a(节点0)的度,M[i][0]中的值为1的元素的个数也为a(节点0)的度,因为在无向图中,邻接矩阵是以对角线对称的。

  • 有向图:M[0]中的值为1的元素的个数为a(节点0)的出度,M[i][0]中的值为1的元素的个数也为a(节点0)的入度

  • 带权图:如果v1到v2有边,则邻接矩阵M[v_1][v_2]=W_1_2, 否则为正无穷

优点:

Read more »

如果要对边进行排序,或按边的权重进行排序,推荐使用边集数组

0%