需要开通vip的题目暂时跳过
点击链接可跳转到所有刷题笔记的导航链接
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
解答
public int calPoints(String[] ops) {
Stack<Integer> stack = new Stack<>();
int res = 0;
for(int i = 0;i < ops.length;i++){
if(ops[i].equals("C")){
res -= stack.pop();
}
else if(ops[i].equals("D")){
Integer peek = stack.peek();
res += peek * 2;
stack.push(peek * 2);
}
else if(ops[i].equals("+")){
Integer pop = stack.pop();
Integer pop2 = stack.pop();
res += pop + pop2;
stack.push(pop2);
stack.push(pop);
stack.push(pop + pop2);
}
else {
int score = Integer.valueOf(ops[i]);
res += score;
stack.push(score);
}
}
return res;
}
分析
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。
解答
public int[] findRedundantConnection(int[][] edges) {
int nodesCount = edges.length;
int[] parent = new int[nodesCount + 1];
int[] sz = new int[nodesCount + 1];
for (int i = 1; i <= nodesCount; i++) {
parent[i] = i;
sz[i] = i;
}
for (int i = 0; i < nodesCount; i++) {
int[] edge = edges[i];
int node1 = edge[0], node2 = edge[1];
if (find(parent, node1) != find(parent, node2)) {
union(parent,sz, node1, node2);
} else {
return edge;
}
}
return new int[0];
}
public void union(int[] parent,int[] sz, int index1, int index2) {
int p = find(parent,index1);
int q = find(parent,index2);
if(sz[p] > sz[q]){
sz[p] += sz[q];
parent[q] = p;
}else{
sz[q] += sz[p];
parent[p] = q;
}
}
public int find(int[] parent, int index) {
if (parent[index] != index) {
parent[index] = find(parent, parent[index]);
}
return parent[index];
}
分析
给定两个字符串 a 和 b,寻找重复叠加字符串 a 的最小次数,使得字符串 b 成为叠加后的字符串 a 的子串,如果不存在则返回 -1。
注意:字符串 “abc” 重复叠加 0 次是 “”,重复叠加 1 次是 “abc”,重复叠加 2 次是 “abcabc”。
解答
public int repeatedStringMatch(String a, String b) {
if(a.contains(b)){
return 1;
}
int min = b.length()/a.length();
StringBuilder sb = new StringBuilder();
for(int i = 1; i <= min+2; i++){
sb.append(a);
if(i>=min && sb.toString().contains(b)){
return i;
}
}
return -1;
}
分析
思路:假设a为abc,b的为cabcabca,这时候b/a=2,就是最少要放两个a,可能a能包含b【此例子不成立,加上后面的条件才能得出答案,这一步只做粗筛选】
综上,只有b/a,b/a+1,b/a+2这三个数可能是正确答案,如果三个数都找不到,那就没有答案了。
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。
解答
int res = 0;
public int longestUnivaluePath(TreeNode root) {
dfs(root);
return res;
}
Map<Integer,Integer> map = new HashMap<>();
public int dfs(TreeNode root) {
if(root == null)return 0;
int left = 0,right = 0;
if(root.left != null){
if(root.val == root.left.val){
left += 1 + dfs(root.left);
}else{
dfs(root.left);
}
}
if(root.right != null){
if(root.val == root.right.val){
right += 1 + dfs(root.right);
}else{
dfs(root.right);
}
}
res = Math.max(res,left + right);
return Math.max(left,right);
}
分析
已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。
现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。
如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。
现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。
求移动结束后,“马” 仍留在棋盘上的概率。
解答
//方法1
public double knightProbability(int N, int K, int rIndex, int cIndex) {
int[][] f = new int[][]{{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};//8个方向
double[][][] memo = new double[N][N][K + 1];
return dfs(N,K,rIndex,cIndex,memo,f);
}
public double dfs(int N,int K,int rIndex,int cIndex,double[][][] memo,int[][] f){
if(rIndex < 0 || rIndex >= N || cIndex < 0 || cIndex >= N)
return 0;
if(K==0)
return 1;
if(memo[rIndex][cIndex][K] != 0)return memo[rIndex][cIndex][K];
double res = 0;
for(int i = 0;i < 8;i++){
res += dfs(N,K-1,rIndex + f[i][0],cIndex + f[i][1],memo,f);
}
memo[rIndex][cIndex][K] = res / 8;
return res / 8;
}
//方法2
public double knightProbability(int N, int K, int rIndex, int cIndex) {
int[][] f = new int[][]{{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};//8个方向
double[][][] dp = new double[N][N][K+1];
for (int r = 0; r < N; r++) {
for (int c = 0; c < N; c++) {
dp[r][c][0] = 1;
}
}
for(int k = 1; k<= K;k++){
for(int r = 0;r < N;r++){
for(int c = 0;c < N;c++){
for(int i = 0;i < 8;i++){
dp[r][c][k] += (r + f[i][0] >= 0 && r + f[i][0] < N && c + f[i][1] >= 0 && c + f[i][1] < N) ? dp[r + f[i][0]][c + f[i][1]][k - 1]: 0;
}
dp[r][c][k] /= 8;
}
}
}
return dp[rIndex][cIndex][K];
}
分析
提交结果
给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。
每个子数组的长度为k,我们要使这3*k个项的和最大化。
返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。
解答
public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
int L = nums.length;
int[] sum = new int[nums.length+1];
for(int i = 0; i < L; i++) {
sum[i+1] = sum[i]+nums[i];
}
// value up to j-th element and with i sub-arrays
int[][] dp = new int[4][L+1];//dp[i][j]表示 在j为止 有i组k大小的子数组和的最大值
for(int i = 1; i < 4; i++) {
for(int j = k; j <= L; j++) {
dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j-k] + sum[j]-sum[j-k]);
}
}
int[] ret = new int[3];
int max = Arrays.stream(dp[3]).max().getAsInt();//3组子数组和的最大值
for(int i = 3; i >= 1; i--) {//遍历子数组的个数 从3->1
for(int j = 1; j <= L; j++) {//从前往后找,第一次出现的最大值
if(dp[i][j] == max){
ret[i-1] = j-k;//记录下最大值数组的起始位置
max -= sum[j]-sum[j-k];// 更新最大值,找下一个子数组和的最大值,例如刚才是找3组 现在是找2组的
break;
}
}
}
return ret;
}
分析
给定一个保存员工信息的数据结构,它包含了员工唯一的id,重要度 和 直系下属的id。
比如,员工1是员工2的领导,员工2是员工3的领导。他们相应的重要度为15, 10, 5。那么员工1的数据结构是[1, 15, [2]],员工2的数据结构是[2, 10, [3]],员工3的数据结构是[3, 5, []]。注意虽然员工3也是员工1的一个下属,但是由于并不是直系下属,因此没有体现在员工1的数据结构中。
现在输入一个公司的所有员工信息,以及单个员工id,返回这个员工和他所有下属的重要度之和。
解答
Map<Integer,Employee> map = new HashMap<>();
public int getImportance(List<Employee> employees, int id) {
int res = 0;
for(Employee employee : employees){
map.put(employee.id,employee);
}
for(Employee employee : employees){
if(employee.id == id){
return dfs(employee);
}
}
return 0;
}
public int dfs(Employee employee){
int res =employee.importance;
for(Integer i:employee.subordinates){
res += dfs(map.get(i));
}
return res;
}
分析
我们给出了 N 种不同类型的贴纸。每个贴纸上都有一个小写的英文单词。
你希望从自己的贴纸集合中裁剪单个字母并重新排列它们,从而拼写出给定的目标字符串 target。
如果你愿意的话,你可以不止一次地使用每一张贴纸,而且每一张贴纸的数量都是无限的。
拼出目标 target 所需的最小贴纸数量是多少?如果任务不可能,则返回 -1。
解答
public static int minStickers(String[] stickers, String target) {
// 过滤判断:如果target中存在贴纸集中没有的字母,直接返回-1
if (existLetterNotInStickers(stickers, target)) return -1;
int[][] stickerMap = new int[stickers.length][26]; // stickerMap[i]:表示第i号贴纸stickers[i]
// 将贴纸集转换成字母频率表示,放入stickerMap
for (int i = 0; i < stickers.length; i++) {
for (char c : stickers[i].toCharArray()) {
stickerMap[i][c-'a']++;
}
}
// 记忆化搜索 target -> 最少贴纸数
HashMap<String, Integer> memo = new HashMap<>();
return minStickers(stickerMap, target, memo);
}
// stickerMap:贴纸集
// target:rest target,要搞定的剩余目标
// 返回搞定target所需要的最少贴纸数
private static int minStickers(int[][] stickerMap, String target, HashMap<String, Integer> memo) {
if (memo.containsKey(target)) return memo.get(target);
if (target.length() == 0) {
memo.put("", 0);
return 0;
}
char[] targetLetters = target.toCharArray(); // target
int[] targetWord = new int[26]; // 以字母频率方式表示target
for (char c : targetLetters) {
targetWord[c-'a']++;
}
int minCount = Integer.MAX_VALUE;
// 枚举第1张使用哪种贴纸
for (int i = 0; i < stickerMap.length; i++) {
int[] sticker = stickerMap[i]; // 决定使用i号贴纸
// 能否使用这种贴纸?
// 如果这种贴纸是"aaabbb",而target是"xyz",使用这种贴纸啥也搞定不了,递归下去就是死循环
// 如果使用这种贴纸,target中至少得有一种字母在这种贴纸中,如果target中的字母在sticker中都不存在,搞定个锤子
if (notContainAnyLetter(sticker, targetLetters)) continue;
// 计算使用这张贴纸后,新的target是啥
// 26种字母依次处理,得到每种字母处理后的字母频率表targetWord
StringBuilder sb = new StringBuilder();
for (int j = 0; j < 26; j++) {
// 如果目标中包含这种字母,使用1张贴纸后,计算这种字母还剩多少个
int restCount = targetWord[j];
if (targetWord[j] != 0) {
restCount = targetWord[j] > sticker[j] ? targetWord[j] - sticker[j] : 0;
}
// 将字母频率转成String:将这种字母添加到StringBuilder
for (int k = 0; k < restCount; k++) {
sb.append((char)(j+'a'));
}
}
String restTarget = sb.toString(); // 使用完第1张贴纸后,剩余目标
int next = minStickers(stickerMap, restTarget, memo); // 【递归】后续需要的贴纸数量
if (next != -1) minCount = Math.min(minCount, next + 1); // 后续过程有效,当前这种方案(使用i号贴纸作为第1张),是一种有效方案
}
int ans = minCount == Integer.MAX_VALUE ? -1 : minCount;
memo.put(target, ans);
return ans;
}
// 【贪心优化】
// 如果target中的字母在sticker中都不存在,返回true
// 如果target中至少存在一种字母在sticker中,返回false
private static boolean notContainAnyLetter(int[] sticker, char[] target) {
char toCheckLetter = target[0];
return sticker[toCheckLetter-'a'] == 0;
}
// 判断target中是否含有贴纸集stickers中没有的字母,如果存在,返回true
private static boolean existLetterNotInStickers(String[] stickers, String target) {
int[] stickersLetters = new int[26];
for (String sticker : stickers) {
for (char letter : sticker.toCharArray()) {
stickersLetters[letter-'a']++;
}
}
for (char letter : target.toCharArray()) {
if (stickersLetters[letter-'a'] == 0) return true;
}
return false;
}
分析
给一非空的单词列表,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。
解答
class Solution {
public List<String> topKFrequent(String[] words, int k) {
List<String> res = new ArrayList<>();
Map<String,Integer> map = new HashMap<>();
for(int i = 0;i < words.length;i++){
map.put(words[i],map.getOrDefault(words[i],0)+1);
}
PriorityQueue<Letter> queue = new PriorityQueue<>(new Comparator<Letter>(){
public int compare(Letter l1,Letter l2){
if(l1.number != l2.number){//频率不同,从大到小排
return l2.number - l1.number;
}
int minL = Math.min(l1.letter.length(),l2.letter.length());
for(int i = 0;i < minL;i++){//遍历
if(l1.letter.charAt(i) == l2.letter.charAt(i))continue;
return l1.letter.charAt(i) - l2.letter.charAt(i);//若字母不同,按字母顺序排序
}
return l1.letter.length() - l2.letter.length();//上面两个都相同的情况下,比较两个单词的长度。短的排前面
}
});
for(String key:map.keySet()){
queue.add(new Letter(key,map.get(key)));
}
while(k > 0){
res.add(queue.poll().letter);
k--;
}
return res;
}
}
class Letter{
String letter;
int number;
public Letter(String letter,int number){
this.letter = letter;
this.number = number;
}
}
分析
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。
解答
public boolean hasAlternatingBits(int n) {
int last = n & 1;
while(n >= 1){
n = n >> 1;
int cur = n & 1;
if(cur == last)return false;
last = cur;
}
return true;
}
分析
给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
解答
int[] f = {1,0,-1,0,1};
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
for(int i = 0;i < grid.length;i++){
for(int j = 0;j < grid[0].length;j++){
if(grid[i][j] != 0)
res = Math.max(res,dfs(grid,i,j));
}
}
return res;
}
public int dfs(int[][] grid,int r,int c){
if(r < 0 || r >= grid.length || c < 0 || c >= grid[0].length || grid[r][c] == 0)return 0;
int res =1
grid[r][c] = 0;//清空,避免重复计算
for(int i = 0;i < 4;i++){//4个方向
res += dfs(grid,r + f[i],c + f[i+1]);
}
return res;
}
分析
给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是连续的。
重复出现的子串要计算它们出现的次数。
解答
public int countBinarySubstrings(String s) {
int zeroIndex = -1,zeroNumber = 0,oneIndex = -1,oneNumber = 0;
char[] chars = s.toCharArray();
int index = 0,res = 0;
while(index < chars.length){
if((chars[index] == '0' && oneIndex > zeroIndex) || (chars[index] == '1' && zeroIndex > oneIndex)){
res += Math.min(zeroNumber,oneNumber);
}
if(chars[index] == '0'){
if(zeroIndex == -1){
zeroIndex = index;
zeroNumber = 1;
}else{
if(zeroIndex > oneIndex){
zeroIndex++;
zeroNumber++;
}else{
zeroIndex = index;
zeroNumber = 1;
}
}
}else{
if(oneIndex == -1){
oneIndex = index;
oneNumber = 1;
}else{
if(oneIndex > zeroIndex){
oneIndex++;
oneNumber++;
}else{
oneIndex = index;
oneNumber = 1;
}
}
}
index++;
}
res += Math.min(oneNumber,zeroNumber);
return res;
}
分析
给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
解答
public int findShortestSubArray(int[] nums) {
if(nums.length < 2)return nums.length;
int[] counts = new int[50000];
int[] first = new int[50000];
int[] dis = new int[50000];
int max = 0;
Arrays.fill(first,-1);
int maxNumber = 0;
for(int i = 0;i < nums.length;i++){
maxNumber = Math.max(maxNumber,nums[i]);
counts[nums[i]]++;
max = Math.max(max,counts[nums[i]]);
if(first[nums[i]] == -1){
first[nums[i]] = i;
}else{
dis[nums[i]] = i - first[nums[i]] + 1;
}
}
int res = Integer.MAX_VALUE;
for(int i = 0;i <= maxNumber;i++){
if(counts[i] == max){
res = Math.min(res,dis[i]);
}
}
return res == 0 ? 1 : res;
}
分析
在无限长的数轴(即 x 轴)上,我们根据给定的顺序放置对应的正方形方块。
第 i 个掉落的方块(positions[i] = (left, side_length))是正方形,其中 left 表示该方块最左边的点位置(positions[i][0]),side_length 表示该方块的边长(positions[i][1])。
每个方块的底部边缘平行于数轴(即 x 轴),并且从一个比目前所有的落地方块更高的高度掉落而下。在上一个方块结束掉落,并保持静止后,才开始掉落新方块。
方块的底边具有非常大的粘性,并将保持固定在它们所接触的任何长度表面上(无论是数轴还是其他方块)。邻接掉落的边不会过早地粘合在一起,因为只有底边才具有粘性。
返回一个堆叠高度列表 ans 。每一个堆叠高度 ans[i] 表示在通过 positions[0], positions[1], …, positions[i] 表示的方块掉落结束后,目前所有已经落稳的方块堆叠的最高高度。
解答
public List<Integer> fallingSquares(int[][] positions) {
List<Integer> hList = new ArrayList<>();//存放当前方块的高度
List<Integer> list = new ArrayList<>();//存放当前最大高度
if(positions == null || positions.length == 0){
return list;
}
int maxH = positions[0][1];
int high = positions[0][1];
hList.add(high);
list.add(maxH);
P:for(int i = 1; i < positions.length; i++){
high = positions[i][1];
//判断当前方块能否放在之前的方块上方,并取得最高值
for(int j = i-1; j>=0; j--){//遍历已经放的块
if(!(positions[j][0] >= positions[i][0] + positions[i][1] || positions[j][0] +positions[j][1]<= positions[i][0])){//若可以放在上面
int temp = hList.get(j) + positions[i][1];//高度叠加
if(temp > high){
high = temp;//更新高度
}
}
}
hList.add(high);//当前块的最终高度
if(high > maxH){
maxH = high;
}
list.add(maxH);//存放当前的最大高度
}
return list;
}
分析
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。