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;
}
}