1.【搜索推荐系统】
https://leetcode-cn.com/problems/search-suggestions-system/
给你一个产品数组 products 和一个字符串 searchWord ,products 数组中每个产品都是一个字符串。
请你设计一个推荐系统,在依次输入单词 searchWord 的每一个字母后,推荐 products 数组中前缀与 searchWord 相同的最多三个产品。如果前缀相同的可推荐产品超过三个,请按字典序返回最小的三个。
请你以二维列表的形式,返回在输入 searchWord 每个字母后相应的推荐产品的列表。
思想:主要是数组要先进行排序Arrays.sort(product) 然后再暴力破解
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class RecommendSystem {
public static void main(String[] args) {
String[] products = {"bags", "baggage", "banner", "box", "cloths"};
String searchWord = "bags";
suggestedProducts(products, searchWord);
}
public static List<List<String>> suggestedProducts(String[] products, String searchWord) {
Arrays.sort(products);
List<List<String>> resultList = new ArrayList<>();
for (int i = 0; i < searchWord.length(); i++) {
List<String> eachResultList = new ArrayList<>();
String tempStr = searchWord.substring(0, i + 1);
int count = 0;
for (int j = 0; j < products.length; j++) {
if (products[j].length() >= (i + 1)) {
String productStrTemp = products[j].substring(0, i + 1);
if (productStrTemp.equals(tempStr)) {
count++;
eachResultList.add(products[j]);
if (count == 3) {
break;
}
}
}
}
resultList.add(eachResultList);
}
return resultList;
}
}
2.下降路径最小和
https://leetcode-cn.com/problems/minimum-falling-path-sum/
给定一个方形整数数组 A,我们想要得到通过 A 的下降路径的最小和。
下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列。
示例:
输入:[[1,2,3],[4,5,6],[7,8,9]]
输出:12
解释:
可能的下降路径有:
[1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9]
[2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9]
[3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9]
和最小的下降路径是 [1,4,7],所以答案是 12。
解题思路:
1.通过动态规划的思想,用 dp(r, c) 表示从位置为 (r, c) 的元素开始的下降路径最小和。根据题目的要求,位置 (r, c) 可以下降到 (r + 1, c - 1),(r + 1, c) 和 (r + 1, c + 1) 三个位置(先不考虑超出数组边界的情况),因此状态转移方程为:dp(r, c) = A[r][c] + min{dp(r + 1, c - 1), dp(r + 1, c), dp(r + 1, c + 1)}
2.查看题目中的要求,计算的是第1行到下面的最短路径,可以从下到上,从倒数第2行开始计算到第1行。
3.最重要的是考虑边界条件
代码如下所示:
public class FallingPathMin {
public static void main(String[] args) {
int[][] tempArr = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
minFallingPathSum(tempArr);
}
public static int minFallingPathSum(int[][] A) {
int size = A.length;
for (int row = size - 2; row >= 0; row--) {
for (int col = 0; col < size; col++) {
int best = 0;
best = A[row + 1][col];
if (col > 0) {
best = Math.min(best, A[row + 1][col - 1]);
}
if (col + 1 < size) {
best = Math.min(best, A[row + 1][col + 1]);
}
A[row][col] += best;
}
}
int min = Integer.MAX_VALUE;
for (int a : A[0]) {
min = Math.min(a, min);
}
return min;
}
}
3.飞地的数量
https://leetcode-cn.com/problems/number-of-enclaves/
给出一个二维数组 A,每个单元格为 0(代表海)或 1(代表陆地)。
移动是指在陆地上从一个地方走到另一个地方(朝四个方向之一)或离开网格的边界。
返回网格中无法在任意次数的移动中离开网格边界的陆地单元格的数量。
示例 1:
输入:[[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
输出:3
解释:
有三个 1 被 0 包围。一个 1 没有被包围,因为它在边界上。
解题思路:
从边界开始遍历,将可以离开网格的单元格都置为0,最后统计为陆地的单元格的数目,则为最后的答案。
如果该单元格为陆地(1),若该单元格如果是边界陆地,则该边界陆地不仅可以离开网格,而且此单元格四个方向的相邻单元格(为陆地的单元格)也可以离开网格边界,如果不为陆地单元,则不做处理,遍历终止。
在编辑过程中,将可以离开网格边界的陆地单元格(从1变成0)
注意:2中单元格四个方向的单元格遍历可以用递归的方式。
代码如下:
package com.company;
public class FeidiCount {
int row, col;
int[][] A;
public static void main(String[] args) {
int[][] arr = {{0, 0, 0, 0}, {1, 0, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}};
new FeidiCount().numEnclaves(arr);
}
public int numEnclaves(int[][] A) {
if (A == null || A.length == 0) return 0;
this.A = A;
this.row = A.length;
this.col = A[0].length;
//边界列
for (int i = 0; i < row; i++) {
dfs(i, 0);
dfs(i, col - 1);
}
//边界行
for (int j = 0; j < col; j++) {
dfs(0, j);
dfs(row - 1, j);
}
int count = 0;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (A[i][j] == 1) {
count++;
}
}
}
System.out.println("count is " + count);
return count;
}
public void dfs(int r, int c) {
if (A[r][c] == 0) return;
if (A[r][c] == 1) A[r][c] = 0;
if (r - 1 > 0) {
dfs(r - 1, c);
}
if (r + 1 < row) {
dfs(r + 1, c);
}
if (c - 1 > 0) {
dfs(r, c - 1);
}
if (c + 1 < col) {
dfs(r, c + 1);
}
}
}
4.复原IP地址方法
https://leetcode-cn.com/problems/restore-ip-addresses/
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
示例:
输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]
代码如下,第一个注释的方法是第一次用暴力法写的,
问题1:没有考虑到001这样的字符串,可以把String转为Integer这样的方式来解决这个问题;
问题2:如果考虑到001这样的字符串,这样的字符串的结果其实是符合最后的结果的,因为去掉了001前面的00,所以筛选的时候还需要加一个判断,就是最终字符串的长度==输入字符串的长度+3
如下代码,第2个方法是暴力破解的方法,可以运行通过的,但是运行时间比较长;
第3个方法是利用StringBuilder来拼接字符串的方法,该方法的运行时间比较短。
package com.company;
import java.util.ArrayList;
import java.util.List;
public class IPZhuhe {
// 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
// 示例:
// 输入: "25525511135"
// 输出: ["255.255.11.135", "255.255.111.35"]
public static void main(String[] args) {
String str = "010010";
restoreIpAddresses(str);
}
//第1个方法
// public static List<String> restoreIpAddresses(String s) {
// List<String> resultList = new ArrayList<>();
// String ip = "";
// int strLength = s.length();
// for (int a = 1; a < 4; a++) {
// for (int b = 1; b < 4; b++) {
// for (int c = 1; c < 4; c++) {
// for (int d = 1; d < 4; d++) {
// if (a + b + c + d == strLength) {
// String aStr = s.substring(0, a);
// String bStr = s.substring(a, a + b);
// String cStr = s.substring(a + b, a + b + c);
// String dStr = s.substring(a + b + c);
// if (Integer.parseInt(aStr) <= 255
// && Integer.parseInt(bStr) <= 255
// && Integer.parseInt(cStr) <= 255
// && Integer.parseInt(dStr) <= 255) {
// String tempStr = aStr + "." + bStr + "." + cStr + "." + dStr;
// System.out.println(tempStr);
// resultList.add(tempStr);
// }
// }
// }
// }
// }
// }
// return resultList;
// }
//"010010"
//输出
//["0.1.0.010","0.1.00.10","0.1.001.0","0.10.0.10","0.10.01.0","0.100.1.0","01.0.0.10","01.0.01.0","01.00.1.0","010.0.1.0"]
//stdout
//0.1.0.010
//0.1.00.10
//第2个方法
public static List<String> restoreIpAddresses(String s) {
List<String> resultList = new ArrayList<>();
int strLength = s.length();
String tempStr = "";
for (int a = 1; a < 4; a++) {
for (int b = 1; b < 4; b++) {
for (int c = 1; c < 4; c++) {
for (int d = 1; d < 4; d++) {
if (a + b + c + d == strLength) {
int aStrInt = Integer.parseInt(s.substring(0, a));
int bStrInt = Integer.parseInt(s.substring(a, a + b));
int cStrInt = Integer.parseInt(s.substring(a + b, a + b + c));
int dStrInt = Integer.parseInt(s.substring(a + b + c));
if (aStrInt <= 255 && bStrInt <= 255 && cStrInt <= 255
&& dStrInt <= 255) {
tempStr = aStrInt + "." + bStrInt + "." + cStrInt + "." + dStrInt;
if (tempStr.length() == strLength + 3) {
System.out.println(tempStr);
resultList.add(tempStr);
}
}
}
}
}
}
}
return resultList;
}
////---第3个方法
// public List<String> restoreIpAddresses(String s) {
// List<String> ret = new ArrayList<>();
//
// StringBuilder ip = new StringBuilder();
//
// for(int a = 1 ; a < 4 ; ++ a)
// for(int b = 1 ; b < 4 ; ++ b)
// for(int c = 1 ; c < 4 ; ++ c)
// for(int d = 1 ; d < 4 ; ++ d)
// {
// if(a + b + c + d == s.length() )
// {
// int n1 = Integer.parseInt(s.substring(0, a));
// int n2 = Integer.parseInt(s.substring(a, a+b));
// int n3 = Integer.parseInt(s.substring(a+b, a+b+c));
// int n4 = Integer.parseInt(s.substring(a+b+c));
// if(n1 <= 255 && n2 <= 255 && n3 <= 255 && n4 <= 255)
// {
// ip.append(n1).append('.').append(n2)
// .append('.').append(n3).append('.').append(n4);
// if(ip.length() == s.length() + 3) ret.add(ip.toString());
// ip.delete(0, ip.length());
// }
// }
// }
// return ret;
// }
}
4.视频拼接
你将会获得一系列视频片段,这些片段来自于一项持续时长为 T
秒的体育赛事。这些片段可能有所重叠,也可能长度不一。
视频片段 clips[i]
都用区间进行表示:开始于 clips[i][0]
并于 clips[i][1]
结束。我们甚至可以对这些片段自由地再剪辑,例如片段 [0, 7]
可以剪切成 [0, 1] + [1, 3] + [3, 7]
三部分。
我们需要将这些片段进行再剪辑,并将剪辑后的内容拼接成覆盖整个运动过程的片段([0, T]
)。返回所需片段的最小数目,如果无法完成该任务,则返回 -1
。
示例 1:
输入:clips = [[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], T = 10
输出:3
解释:
我们选中 [0,2], [8,10], [1,9] 这三个片段。
然后,按下面的方案重制比赛片段:
将 [1,9] 再剪辑为 [1,2] + [2,8] + [8,9] 。
现在我们手上有 [0,2] + [2,8] + [8,10],而这些涵盖了整场比赛 [0, 10]。
示例 2:
输入:clips = [[0,1],[1,2]], T = 5
输出:-1
解释:
我们无法只用 [0,1] 和 [0,2] 覆盖 [0,5] 的整个过程。
示例 3:
输入:clips = [[0,1],[6,8],[0,2],[5,6],[0,4],[0,3],[6,7],[1,3],[4,7],[1,4],[2,5],[2,6],[3,4],[4,5],[5,7],[6,9]], T = 9
输出:3
解释:
我们选取片段 [0,4], [4,7] 和 [6,9] 。
示例 4:
输入:clips = [[0,4],[2,8]], T = 5
输出:2
解释:
注意,你可能录制超过比赛结束时间的视频。
解题思路:
利用优先队列的思想,先按照clips[i][0]升序排列,然后按照clips[i][1]降序排列
最后再根据优先队列和贪心算法的特性,在已选区间最后边界为last , 贪心地选后面区间开头能overlap last的区间能够覆盖到最远距离的区间。
直到last >= T为止。
若找不到overlap区间则无解,返回-1。
package com.company;
import java.util.Comparator;
import java.util.PriorityQueue;
public class ShipinPiejie {
public static void main(String[] args) {
int[][] clips = {{0, 2}, {4, 6}, {8, 10}, {1, 9}, {1, 5}, {5, 9}};
int T = 10;
videoStitching(clips, 10);
}
public static int videoStitching(int[][] clips, int T) {
Comparator<int[]> c = new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[0] != o2[0]) {
return o1[0] - o2[0];
} else {
return o2[1] - o1[1];
}
}
};
int ans = 0;
PriorityQueue<int[]> p = new PriorityQueue<>(c);
for (int i = 0; i < clips.length; i++) {
p.add(clips[i]);
}
int cur = 0;
int next = 0;
while (next < T && !p.isEmpty() && next >= p.peek()[0]) {
cur = next;
ans++;
while (!p.isEmpty() && p.peek()[0] <= cur) {
next = Math.max(next, p.poll()[1]);
}
}
if (next < T) {
return -1;
} else {
return ans;
}
}
}
5.零钱兑换
https://leetcode-cn.com/problems/coin-change/solution/322-ling-qian-dui-huan-by-leetcode-solution/
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
解题思路:利用动态规划的思想,求解最优解,采用自下而上的方法,分别计算最优解。
package com.company;
//给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。
// 如果没有任何一种硬币组合能组成总金额,返回 -1。
import java.util.Arrays;
public class MoneyChange {
public static void main(String[] args) {
int[] coins = {1, 2, 5};
int amount = 11;
System.out.println(coinChange(coins, amount));
}
public static int coinChange(int[] coins, int amount) {
int max = amount + 1;
int[] dpList = new int[amount + 1];
Arrays.fill(dpList, max);
dpList[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int coin : coins) {
if (coin <= i) {
int prev = i - coin;
dpList[i] = Math.min(dpList[prev] + 1, dpList[i]);
}
}
}
return dpList[amount] >= amount + 1 ? -1 : dpList[amount];
}
}
6.可以攻击国王的皇后
https://leetcode-cn.com/problems/queens-that-can-attack-the-king/
在一个 8x8 的棋盘上,放置着若干「黑皇后」和一个「白国王」。
「黑皇后」在棋盘上的位置分布用整数坐标数组 queens 表示,「白国王」的坐标用数组 king 表示。
「黑皇后」的行棋规定是:横、直、斜都可以走,步数不受限制,但是,不能越子行棋。
请你返回可以直接攻击到「白国王」的所有「黑皇后」的坐标(任意顺序)。
输入:queens = [[0,1],[1,0],[4,0],[0,4],[3,3],[2,4]], king = [0,0]
输出:[[0,1],[1,0],[3,3]]
解释:
[0,1] 的皇后可以攻击到国王,因为他们在同一行上。
[1,0] 的皇后可以攻击到国王,因为他们在同一列上。
[3,3] 的皇后可以攻击到国王,因为他们在同一条对角线上。
[0,4] 的皇后无法攻击到国王,因为她被位于 [0,1] 的皇后挡住了。
[4,0] 的皇后无法攻击到国王,因为她被位于 [1,0] 的皇后挡住了。
[2,4] 的皇后无法攻击到国王,因为她和国王不在同一行/列/对角线上。
解题思路:
设置一个标志数组,将国王的位置标记;
从皇后的位置,向8个方向分别遍历搜索,如果发现有被标志的位置,则添加到最后结果里面,该方向遍历结束。
这里有个小技巧是:一维数组可以直接转换为List,通过Arrays.asList(x,y)
代码如下:
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2020. All rights reserved.
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* The type King queen.
*/
public class KingQueen {
/**
* The entry point of application.
*
* @param args the input arguments
*/
public static void main(String[] args) {
int[][] queens={{0,0},{1,1},{2,2},{3,4},{3,5},{4,4},{4,5}};
int [] king={3,3};
queensAttacktheKing(queens,king);
}
/**
* Queens attackthe king list.
*
* @param queens the queens
* @param king the king
* @return the list
*/
public static List<List<Integer>> queensAttacktheKing(int[][] queens, int[] king) {
final int size = 8;
int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {-1, -1}, {1, 1}, {1, -1}, {-1, 1}};
int[][] qiPanFlag = new int[size][size];
for (int i = 0; i < queens.length; i++) {
int x = queens[i][0];
int y = queens[i][1];
qiPanFlag[x][y] = 1;
}
List<List<Integer>> resultList = new ArrayList<>();
for (int i = 0; i < directions.length; i++) {
int direcX = king[0];
int direcY = king[1];
// List<Integer> tempList = new ArrayList<>();
for (int tempX = direcX, tempY = direcY; tempX >= 0 && tempX < size && tempY >= 0 && tempY < size; tempX +=directions[i][0], tempY += directions[i][1]) {
if (qiPanFlag[tempX][tempY] == 1) {
// tempList.add(tempX);
// tempList.add(tempY);
// resultList.add(tempList);
resultList.add(Arrays.asList(tempX,tempY));
break;
}
}
}
return resultList;
}
}
7.最大宽度坡
https://leetcode-cn.com/problems/maximum-width-ramp/
给定一个整数数组 A,坡是元组 (i, j),其中 i < j 且 A[i] <= A[j]。这样的坡的宽度为 j - i。
找出 A 中的坡的最大宽度,如果不存在,返回 0 。
示例 1:
输入:[6,0,8,2,1,5]
输出:4
解释:
最大宽度的坡为 (i, j) = (1, 5): A[1] = 0 且 A[5] = 5.
解题思路:
1.用暴力破解方法解题发现会超时
2.使用单调栈的方法解题:
1.找出从前到后的单调递减序列;
2.从后向前遍历,找到每个满足条件的宽度,比较大小
代码如下:
package com.company;
import java.util.Arrays;
import java.util.Stack;
public class MaxWidthPo {
public static void main(String[] args) {
int[] a = {6, 0, 8, 2, 1, 5};
new MaxWidthPo().maxWidthRamp(a);
}
// public int maxWidthRamp(int[] A) {
// int max=0;
// for(int i=0;i<A.length-1;i++) {
// for(int j=A.length-1;j>i;j--) {
// if(A[i]<=A[j]) {
// max=Math.max(max,j-i);
// continue;
// }
// }
// }
//
// return max;
// }
public int maxWidthRamp(int[] A) {
Stack<Integer> stack = new Stack<>();
stack.push(0);
//先找出单调递减子序列
for (int i = 1; i < A.length; i++) {
if (A[stack.peek()] > A[i]) {
stack.push(i);
}
}
int maxWidth = 0;
//然后从后往前遍历,找到最宽的子序列
for (int j = A.length - 1; j >= 0; j--) {
while (!stack.isEmpty() && A[j] >= A[stack.peek()]) {
maxWidth = Math.max(maxWidth, j - stack.pop());
}
}
return maxWidth;
}
}
利用单调栈的思想解决的还有下面题目:
https://leetcode-cn.com/problems/longest-well-performing-interval/
表现良好的最长时间段:
给你一份工作时间表 hours
,上面记录着某一位员工每天的工作小时数。
我们认为当员工一天中的工作小时数大于 8
小时的时候,那么这一天就是「劳累的一天」。
所谓「表现良好的时间段」,意味在这段时间内,「劳累的天数」是严格 大于「不劳累的天数」。
请你返回「表现良好时间段」的最大长度。
class Solution {
public int longestWPI(int[] hours) {
for (int i = 0; i < hours.length; i++) {
hours[i] = hours[i]>8?1:-1;
}
int[] score =new int[hours.length+1];score[0]=0;
for (int i = 1; i < score.length; i++) {
score[i]=score[i-1]+hours[i-1];
}
//递减栈
Stack<Integer> stack =new Stack<>();
for (int i = 0; i < score.length; i++) {
if(!stack.empty()&&score[stack.peek()]<=score[i])
continue;
stack.push(i);
}
int ans = 0;
for (int i = score.length-1; i >= 0; i--) {
if(score[i]>score[stack.peek()]){
ans=Math.max(ans,i-stack.peek());
stack.pop();i++;
if(stack.empty()) break;
continue;
}
}
return ans;
}
}