10.10 华为题

This commit is contained in:
zhangsan 2025-10-10 15:21:21 +08:00
parent 425feff3c7
commit 3d5587b297
7 changed files with 519 additions and 22 deletions

View File

@ -0,0 +1,85 @@
package All;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
/**
* 题目 950. 按递增顺序显示卡牌
* 描述牌组中的每张卡牌都对应有一个唯一的整数你可以按你想要的顺序对这套卡片进行排序
* 最初这些卡牌在牌组里是正面朝下的未显示状态
* 现在重复执行以下步骤直到显示所有卡牌为止
* 从牌组顶部抽一张牌显示它然后将其从牌组中移出
* 如果牌组中仍有牌则将下一张处于牌组顶部的牌放在牌组的底部
* 如果仍有未显示的牌那么返回步骤 1否则停止行动
* 返回能以递增顺序显示卡牌的牌组顺序
* 答案中的第一张牌被认为处于牌堆顶部
示例 1
输入[17,13,11,2,3,5,7]
输出[2,13,3,11,5,17,7]
解释
我们得到的牌组顺序为 [17,13,11,2,3,5,7]这个顺序不重要然后将其重新排序
重新排序后牌组以 [2,13,3,11,5,17,7] 开始其中 2 位于牌组的顶部
我们显示 2然后将 13 移到底部牌组现在是 [3,11,5,17,7,13]
我们显示 3并将 11 移到底部牌组现在是 [5,17,7,13,11]
我们显示 5然后将 17 移到底部牌组现在是 [7,13,11,17]
我们显示 7并将 13 移到底部牌组现在是 [11,17,13]
我们显示 11然后将 17 移到底部牌组现在是 [13,17]
我们展示 13然后将 17 移到底部牌组现在是 [17]
我们显示 17
由于所有卡片都是按递增顺序排列显示的所以答案是正确的
* 链接https://leetcode.cn/problems/reveal-cards-in-increasing-order/
*/
//不会
public class DeckRevealedIncreasing {
/**
* 希望显示出来的牌是 [2, 3, 5, 7, 11, 13, 17]
* 要求反推出原始牌堆的顺序
* 原始牌堆 [2,13,3,11,5,17,7]
* 显示顺序 [2,3,5,7,11,13,17]
*
* 核心思路逆推法Reverse Simulation
* 我们从结果往回推反向模拟
* 显示顺序是 [2,3,5,7,11,13,17]升序
* 那么
* 最后显示的牌是 17说明它最初在最底
* 在它之前显示的是 13说明在那时
* 17 移到牌堆顶
* 然后把 13 放到牌堆顶
* 依次反推即可
*/
public int[] deckRevealedIncreasing(int[] deck) {
Arrays.sort(deck); // 升序排列我们希望显示顺序是递增的
Deque<Integer> deque = new LinkedList<>();
// 从最后一张牌开始反向构建队列
for (int i = deck.length - 1; i >= 0; i--) {
if (!deque.isEmpty()) {
// 1 将队尾的牌移动到队首模拟倒回去的操作
Integer last = deque.pollLast(); // 安全取出队尾为空时返回 null
deque.offerFirst(last); // 插入到队首
}
// 2 把当前牌放到队首
deque.offerFirst(deck[i]);
}
// 3 把双端队列转成数组输出
int[] res = new int[deck.length];
int idx = 0;
while (!deque.isEmpty()) {
res[idx++] = deque.pollFirst(); // 从队首依次取出
}
return res;
}
public static void main(String[] args) {
DeckRevealedIncreasing solution = new DeckRevealedIncreasing();
int[] deck = {17,13,11,2,3,5,7};
System.out.println(Arrays.toString(solution.deckRevealedIncreasing(deck)));
// 输出: [2, 13, 3, 11, 5, 17, 7]
}
}

View File

@ -0,0 +1,63 @@
package All;
import java.util.Arrays;
/**
* 题目 43. 字符串相乘
* 描述给定两个以字符串形式表示的非负整数 num1 num2返回 num1 num2 的乘积它们的乘积也表示为字符串形式
* 注意不能使用任何内置的 BigInteger 库或直接将输入转换为整数
示例 1
输入: num1 = "2", num2 = "3"
输出: "6"
* 链接https://leetcode.cn/problems/multiply-strings/
*/
//不断调试出来了
public class Multiply {
public String multiply(String num1, String num2) {
if (num1.equals("0") || num2.equals("0")) return "0";
int len1 = num1.length(), len2 = num2.length();
int[] temp = new int[len1 + len2 + 1];
Arrays.fill(temp, -1);
// 累加各位乘积
for (int i = len1 - 1; i >= 0; i--) {
int start = len1 - i - 1;
int a = num1.charAt(i) - '0';
for (int j = len2 - 1; j >= 0; j--) {
int b = num2.charAt(j) - '0';
if (temp[start] == -1) temp[start] = 0;
temp[start] += a * b;
start++;
}
}
// 进位归一化
int carry = 0, used = 0;
for (; used < temp.length; used++) {
if (temp[used] == -1) break;
int cur = temp[used] + carry;
temp[used] = cur % 10;
carry = cur / 10;
}
while (carry > 0) {
temp[used++] = carry % 10;
carry /= 10;
}
// 构造结果从低位到高位拼接后反转
StringBuilder sb = new StringBuilder(used);
for (int i = used - 1; i >= 0; i--) {
sb.append(temp[i]);
}
return sb.toString();
}
public static void main(String[] args) {
String num1 = "123", num2 = "456";
Multiply multiply = new Multiply();
System.out.println(multiply.multiply(num1, num2)); // 56088
}
}

View File

@ -0,0 +1,113 @@
package All;
import java.util.*;
/**
* 题目 792. 匹配子序列的单词数
* 描述给定字符串 s 和字符串数组 words, 返回 words[i] 中是s的子序列的单词个数
* 字符串的 子序列 是从原始字符串中生成的新字符串可以从中删去一些字符(可以是none)而不改变其余字符的相对顺序
* 例如 ace abcde 的子序列
*
示例 1
输入: s = "abcde", words = ["a","bb","acd","ace"]
输出: 3
解释: 有三个是 s 的子序列的单词: "a", "acd", "ace"
* 链接https://leetcode.cn/problems/number-of-matching-subsequences/
*/
//不会
public class NumMatchingSubseq {
/*
预处理 s
对于 s 中的每个字符记录它在 s 中所有出现的位置升序存储
例如 s = "abcde"
'a' -> [0]
'b' -> [1]
'c' -> [2]
'd' -> [3]
'e' -> [4]
检查一个单词 word 是否为子序列
用一个变量 prev 记录上一个匹配到的下标初始为 -1
word 的每个字符 c
posMap[c] 的下标列表中二分查找 第一个 (prev+1) 的位置
如果找不到说明 word 不是子序列
如果找到了更新 prev 为这个下标
如果整个 word 都能找到合适位置则说明它是子序列
统计结果
遍历 words逐个判断是否是子序列统计个数
*/
public int numMatchingSubseq(String s, String[] words) {
// 预处理 s对每个字符记录它在 s 中出现的所有下标升序
Map<Character, List<Integer>> posMap = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 如果 posMap 中还没有这个字符先建一个空列表
if (!posMap.containsKey(c)) {
posMap.put(c, new ArrayList<>());
}
// 把当前位置 i 添加进去
posMap.get(c).add(i);
}
int count = 0; // 统计有多少个 word 是子序列
for (String word : words) {
if (isSubsequence(word, posMap)) {
count++;
}
}
return count;
}
/**
* lowerBound: list 中二分查找第一个 >= target 的元素下标
* 如果所有元素都 < target返回 list.size()表示没找到
*/
private int lowerBound(List<Integer> list, int target) {
int left = 0, right = list.size() - 1;
while (left <= right) { // 注意这里是 <=
int mid = left + (right - left) / 2;
if (list.get(mid) < target) {
left = mid + 1; // 舍弃左半部分
} else {
right = mid - 1; // mid 也可能满足条件缩小右边界
}
}
return left; // 最终 left 就是第一个 >= target 的位置
}
/**
* 判断 word 是否是 s 的子序列
*/
private boolean isSubsequence(String word, Map<Character, List<Integer>> posMap) {
int prev = -1; // 上一个字符在 s 中匹配到的位置初始为 -1表示还没匹配
for (char c : word.toCharArray()) {
// 如果 s 中根本没有字符 c直接返回 false
if (!posMap.containsKey(c)) return false;
// list 保存了 c s 中所有出现位置
List<Integer> list = posMap.get(c);
// list 中找一个 >= prev+1 的位置保证相对顺序
int idx = lowerBound(list, prev + 1);
// 如果没找到idx == list.size()说明 word 不是子序列
if (idx == list.size()) return false;
// 更新 prev记录当前字符在 s 中匹配到的位置
prev = list.get(idx);
}
return true; // 所有字符都能找到合适位置说明 word 是子序列
}
// 测试
public static void main(String[] args) {
NumMatchingSubseq solver = new NumMatchingSubseq();
String s = "abcde";
String[] words = {"a", "bb", "acd", "ace"};
System.out.println(solver.numMatchingSubseq(s, words)); // 输出 3
}
}

View File

@ -14,30 +14,28 @@ package All;
* 链接https://leetcode.cn/problems/solve-the-equation/
*/
public class SolveEquation {
class Solution {
public String solveEquation(String s) {
int x = 0, num = 0, n = s.length();
char[] cs = s.toCharArray();
for (int i = 0, op = 1; i < n; ) {
if (cs[i] == '+') {
op = 1; i++;
} else if (cs[i] == '-') {
op = -1; i++;
} else if (cs[i] == '=') {
x *= -1; num *= -1; op = 1; i++;
} else {
int j = i;
//找当前数字/变量的边界 比如 "3x""x""5"
while (j < n && cs[j] != '+' && cs[j] != '-' && cs[j] != '=') j++;
//"3x" "3" "x" 前面没数字这时就走 else 1表示系数是 1
if (cs[j - 1] == 'x') x += (i < j - 1 ? Integer.parseInt(s.substring(i, j - 1)) : 1) * op;
public String solveEquation(String s) {
int x = 0, num = 0, n = s.length();
char[] cs = s.toCharArray();
for (int i = 0, op = 1; i < n; ) {
if (cs[i] == '+') {
op = 1; i++;
} else if (cs[i] == '-') {
op = -1; i++;
} else if (cs[i] == '=') {
x *= -1; num *= -1; op = 1; i++;
} else {
int j = i;
//找当前数字/变量的边界 比如 "3x""x""5"
while (j < n && cs[j] != '+' && cs[j] != '-' && cs[j] != '=') j++;
//"3x" "3" "x" 前面没数字这时就走 else 1表示系数是 1
if (cs[j - 1] == 'x') x += (i < j - 1 ? Integer.parseInt(s.substring(i, j - 1)) : 1) * op;
//如果不是 x 就说明是数字比如 "5""10"
else num += Integer.parseInt(s.substring(i, j)) * op;
i = j;
}
else num += Integer.parseInt(s.substring(i, j)) * op;
i = j;
}
if (x == 0) return num == 0 ? "Infinite solutions" : "No solution";
else return "x=" + (num / -x);
}
if (x == 0) return num == 0 ? "Infinite solutions" : "No solution";
else return "x=" + (num / -x);
}
}

View File

@ -0,0 +1,88 @@
package All;
/**
* 题目 37. 解数独
* 描述编写一个程序通过填充空格来解决数独问题
* 数独的解法需 遵循如下规则
* 数字 1-9 在每一行只能出现一次
* 数字 1-9 在每一列只能出现一次
* 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次请参考示例图
* 数独部分空格内已填入了数字空白格用 '.' 表示
* 链接https://leetcode.cn/problems/sudoku-solver/
*/
//不会
public class SolveSudoku {
// 宫格的标记数组
private boolean[][] row = new boolean[9][10];
private boolean[][] col = new boolean[9][10];
private boolean[][] box = new boolean[9][10];
public void solveSudoku(char[][] board) {
// 初始化标记
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.') {
int num = board[i][j] - '0';
row[i][num] = true;
col[j][num] = true;
box[(i / 3) * 3 + (j / 3)][num] = true;
}
}
}
backtrack(board, 0, 0);
}
// 回溯 (i,j) 开始填充
private boolean backtrack(char[][] board, int i, int j) {
// 到达终点
if (i == 9) return true;
// 下一个位置 到了本行的最后一列 (0,8)下一个位置应该跳到 下一行的开头 (1,0)
int nextI = j == 8 ? i + 1 : i;
int nextJ = j == 8 ? 0 : j + 1;
if (board[i][j] != '.') {
return backtrack(board, nextI, nextJ);
}
// 尝试 1-9
for (int num = 1; num <= 9; num++) {
int boxIndex = (i / 3) * 3 + (j / 3);
if (!row[i][num] && !col[j][num] && !box[boxIndex][num]) {
// 放置
board[i][j] = (char) (num + '0');
row[i][num] = col[j][num] = box[boxIndex][num] = true;
if (backtrack(board, nextI, nextJ)) return true;
// 撤销
board[i][j] = '.';
row[i][num] = col[j][num] = box[boxIndex][num] = false;
}
}
return false; // 无解
}
// 测试
public static void main(String[] args) {
char[][] board = {
{'5','3','.','.','7','.','.','.','.'},
{'6','.','.','1','9','5','.','.','.'},
{'.','9','8','.','.','.','.','6','.'},
{'8','.','.','.','6','.','.','.','3'},
{'4','.','.','8','.','3','.','.','1'},
{'7','.','.','.','2','.','.','.','6'},
{'.','6','.','.','.','.','2','8','.'},
{'.','.','.','4','1','9','.','.','5'},
{'.','.','.','.','8','.','.','7','9'}
};
SolveSudoku solver = new SolveSudoku();
solver.solveSudoku(board);
// 打印解后的数独
for (char[] row : board) {
for (char c : row) System.out.print(c + " ");
System.out.println();
}
}
}

View File

@ -0,0 +1,67 @@
package All;
/**
* 题目 1588. 所有奇数长度子数组的和
* 描述给你一个正整数数组 arr 请你计算所有可能的奇数长度子数组的和
* 子数组 定义为原数组中的一个连续子序列
* 请你返回 arr 所有奇数长度子数组的和
示例 1
输入arr = [1,4,2,5,3]
输出58
解释所有奇数长度子数组和它们的和为
[1] = 1
[4] = 4
[2] = 2
[5] = 5
[3] = 3
[1,4,2] = 7
[4,2,5] = 11
[2,5,3] = 10
[1,4,2,5,3] = 15
我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58
* 链接https://leetcode.cn/problems/sum-of-all-odd-length-subarrays/
*/
//不会高效做法
public class SumOddLengthSubarrays {
/*
前缀和数组的作用
我们先构造一个前缀和数组 prefix其中 prefix[i] 表示 arr[0..i-1] 的元素和
那么区间 [l, r] 的子数组和就可以快速得到
sum(l, r) = prefix[r+1] - prefix[l]
遍历所有奇数长度子数组
子数组由 (l, r) 确定长度为 r - l + 1
我们只关心长度为奇数的情况
遍历所有可能的起点 l再遍历所有终点 r保证长度奇数利用前缀和快速算出区间和加到答案里
*/
public int sumOddLengthSubarrays(int[] arr) {
int n = arr.length;
// 前缀和数组多开一位prefix[0] = 0
int[] prefix = new int[n + 1];
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + arr[i];
}
int ans = 0;
// 枚举所有子数组的起点
for (int l = 0; l < n; l++) {
// 枚举终点
for (int r = l; r < n; r++) {
int len = r - l + 1;
if (len % 2 == 1) { // 只处理奇数长度
// 区间和 = prefix[r+1] - prefix[l]
ans += prefix[r + 1] - prefix[l];
}
}
}
return ans;
}
public static void main(String[] args) {
SumOddLengthSubarrays solver = new SumOddLengthSubarrays();
int[] arr = {1, 4, 2, 5, 3};
System.out.println(solver.sumOddLengthSubarrays(arr)); // 输出 58
}
}

View File

@ -0,0 +1,83 @@
package All;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 题目 611. 有效三角形的个数
* 描述给定一个包含非负整数的数组 nums 返回其中可以组成三角形三条边的三元组个数
示例 1
输入: nums = [2,2,3,4]
输出: 3
解释:有效的组合是:
2,3,4 (使用第一个 2)
2,3,4 (使用第二个 2)
2,2,3
* 链接https://leetcode.cn/problems/number-of-matching-subsequences/
*/
public class TriangleNumber {
//回溯法超时了
int count=0;
void backtrack(int[] nums, int start, List<Integer> path){
for (int i = start; i < nums.length; i++) {
if(nums[i]==0)continue;
if(path.size()==2){
int first=path.get(0);
int second=path.get(1);
if(first+second<=nums[i])
break;
else {
count++;
continue;
}
}
path.add(nums[i]);
backtrack(nums,i+1,path);
path.remove(path.size()-1);
}
}
public int triangleNumber1(int[] nums) {
Arrays.sort(nums);
List<Integer>path=new ArrayList<>();
backtrack(nums,0,path);
return count;
}
//双指针法
//高效的关键是固定最大边而不是最小边次小边
public int triangleNumber(int[] nums) {
Arrays.sort(nums);
int count = 0;
int n = nums.length;
// 固定最长边 nums[k]
for (int k = n - 1; k >= 2; k--) {
int i = 0, j = k - 1;
while (i < j) {
// 如果 nums[i] + nums[j] > nums[k]
if (nums[i] + nums[j] > nums[k]) {
// 那么 [i, j-1] 都满足条件
count += (j - i);
j--;
} else {
// 否则 i 太小往右移动
i++;
}
}
}
return count;
}
public static void main(String[] args) {
TriangleNumber solution = new TriangleNumber();
int[] nums= {2,2,3,4};
int res=solution.triangleNumber(nums);
System.out.println(res);
}
}