华为OD高频面试题-手撕代码(二)

全排列

乘积小于 K 的子数组

子数组按位或操作

我们有一个非负整数数组 arr 。

对于每个(连续的)子数组 sub = [arr[i], arr[i + 1], ..., arr[j]] ( i <= j),我们

对 sub 中的每个元素进行按位或操作,获得结果 arr[i] | arr[i + 1] | ... | arr[j] 。

返回可能结果的数量。 多次出现的结果在最终答案中仅计算一次。

示例 1:

输入:arr = [0]

输出:1

解释:

只有一个可能的结果 0 。

示例 2:

输入:arr = [1,1,2]

输出:3

解释:

可能的子数组为 [1],[1],[2],[1, 1],[1, 2],[1, 1, 2]。

产生的结果为 1,1,2,1,3,3 。

有三个唯一值,所以答案是 3 。

示例 3:

输入:arr = [1,2,4]

输出:6

解释:

可能的结果是 1,2,3,4,6,以及 7 。

代码实现

public class Main {
//题目理解:在A数组中获取连续的子数组B,对子数组进行或运算之后,保存或运算之后的结果
//程序执行流程:以 [1,1,2]为例
//1、先将1放入cur中(cur保存的是上一次异或的结果),因为第1次子数组只有一个1,所以直
接放入cur中。
//2、再循环第二个1的时候,cur中已经有一个1了,cur中的所有值和1进行或运算,并将中间结
果保存到tmp中
//3、将每次或运算的结果存入res中(因为结果不能重复所以res使用的是hashset存储的)
//4、再将当前的x存入到cur中,避免只有一个元素的子数组的情况发生
//5、最终返回res的大小即可。
//cur中保存的是以前的结果,每次会向cur中添加一个值的,就相当于子数组了
public static int subarrayBitwiseORs(int[] nums) {
HashSet<Integer> res = new HashSet<>();//因为结果不能重复,所以使用set来
保存结果
HashSet<Integer> cur = new HashSet<>();//存储上一次或运算的结果
cur.add(0);
for (int x : nums) {
HashSet<Integer> tmp = new HashSet<>();// 存储当前值的结果
for (int y : cur) {//枚举x及之前的元素的所有异或值
tmp.add(x | y);
}
tmp.add(x);
cur = tmp;
res.addAll(cur);
}
return res.size();
}
public static void main(String[] args) {
int[] nums = {1, 1, 2};
System.out.println(subarrayBitwiseORs(nums));
}
}

划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。

注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。

示例 1:

输入:s = "ababcbacadefegdehijhklij"

输出:[9,7,8]

解释:

划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。

每个字母最多出现在一个片段中。

像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。

示例 2:

输入:s = "eccbbbbdec"

输出:[10]

提示:

1 <= s.length <= 500

s 仅由小写英文字母组成

public class Main {
//题目含义:将一个字符串划分成多个子字符串,每个子字符串不能和其他字符串存在相同的字
符,而且分割后的多个子字符串可以合成原始的字符串
//思路:
// 1、统计出每个字符在字符串中出现的最后位置到hash数组中(相当于找打了每个字符最后出
现的位置)
// 2、使用双指针算法,循环原始字符串中的每一个字符,如果i还未到达hash中的位置,则更新
右指针
//3、直到i和hash中的位置相等时,记录一次子串的长度,左指针移动到右指针的下一位,继续
循环
//例如: ababcbacadefegdehijhklij
//1、先统计出每个字符出现的次数,a 最后出现的下标是 8
// b 最后出现的下标是 5
// c 最后出现的下标是 7 后边依次类推
//2、定义左右指针默认位置都在0位置 进入循环后,在hash中取得当前字符的最后一次出现的
位置和右指针比较
//a 在hash中的位置是8,所以移动右指针end 到 8
//b 在hash中的位置是5,所以右指针不动
//c 在hash中的位置是7,所以右指针也不动
//3、直到发现当前循环变量i 和右指针相等了,说明已经找到了一组子字符串和其他的字符串不
存在相同的字符了,将长度添加到结果中
//4、更新左指针到右指针的下一个位置,继续循环,直到将所有子字符串长度获取到为止
public static List<Integer> partitionLabels(String s) {
List<Integer> res = new ArrayList<>();
// 1.统计字母最后一次出现的位置
int[] hash = new int[26];
for (int i = 0; i < s.length(); i++) {
hash[s.charAt(i) - 'a'] = i;
}
// 2.划分区间 ababcbaca defegde hijhklij
int start = 0;
int end = 0;
for (int i = 0; i < s.length(); i++) {
int pos = hash[s.charAt(i) - 'a'];
if (end < pos) {
end = pos;
}
if (i == end) {
res.add(end - start + 1);
start = end + 1;
}
}
return res;
}
public static void main(String[] args) {
String s = "ababcbacadefegdehijhklij";
System.out.println(partitionLabels(s));
}
}

对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

判断一个二叉树是否对称
分为三个情况
1、左右节点有的值相同时,分别判断左右子树是否相等
2、左右节点均为空,返回true时,返回为真
3、左右节点值不相同的时候,返回为false
public boolean isSymmetric(TreeNode root) {
if (root == null) return true; // 如果根节点为空,返回true,因为空树被认为是对
称的
return check(root.left, root.right); // 调用check函数检查左右子树是否对称
}
public boolean check(TreeNode left, TreeNode right) {
// 结构上不成立,一个节点为空,另一个不为空,返回false
if (left == null && right != null || left != null && right == null) {
return false;
}
// 左右节点均为空,返回true
if (left == right && left == null) {
return true;
} else if (left.val == right.val) { // 值相同,继续递归判断
return (check(left.left, right.right) && check(left.right,
right.left));
}
// 值不同,返回false
else return false;
}

单字符重复子串的最大长度

如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。

给你一个字符串 text,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子

串。返回其中最长的子串的长度。

示例 1:

输入:text = "ababa"

输出:3

示例 2:

输入:text = "aaabaaa"

输出:6

提示:

1 <= text.length <= 20000

text 仅由小写英文字母组成。

class Solution {
public int maxRepOpt1(String text) {
Map<Character, Integer> count = new HashMap<Character, Integer>();
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
count.put(c, count.getOrDefault(c, 0) + 1);
}
int res = 0;
for (int i = 0; i < text.length(); ) {
// step1: 找出当前连续的一段 [i, j)
int j = i;
while (j < text.length() && text.charAt(j) == text.charAt(i)) {
j++;
}
int curCnt = j - i;
// step2: 如果这一段长度小于该字符出现的总数,并且前面或后面有空位,则使用
curCnt + 1 更新答案
if (curCnt < count.getOrDefault(text.charAt(i), 0) && (j <
text.length() || i > 0)) {
res = Math.max(res, curCnt + 1);
}
// step3: 找到这一段后面与之相隔一个不同字符的另一段 [j + 1, k),如果不存
在则 k = j + 1
int k = j + 1;
while (k < text.length() && text.charAt(k) == text.charAt(i)) {
k++;
}
res = Math.max(res, Math.min(k - i,
count.getOrDefault(text.charAt(i), 0)));
i = j;
}
return res;
}
}

路径总和Ⅱ

思路:要使用深度优先算法来实现
1、从二叉树的根节点开始进行遍历
2、记录每次遍历的节点值,保存到path中,并在targetSum和中减掉当前节点的值
3、如果sum已经是0了,并且左右节点都是空,说明找到了一组,添加到ans
4、递归找左右节点
5、左右节点递归完成后,把第2步保存的值删除掉,称为回溯
举例:比如这个用例 targetSum = 22
1、先将5添加到path中,targetSum = 22 减掉 5后是17
2、再去递归4这个左节点,给targetSum见到13
3、再去递归11这个节点,给targetSum减到2
4、再去递归7这个节点,但是减完之后是-6,不满足条件。这个时候左右节点都是空的,递归结束
5、当递归2节点的时候,targetSum减完之后刚好是0,所以把
所以把5,4,11,2添加到ans结果中
6、直到把所有节点都递归完后,可以得到两个结果
public class Main {
public static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root, targetSum);
return ans;
}
void dfs(TreeNode root, int targetSum) {
if (root == null) {
return;
}
path.add(root.val);
targetSum -= root.val;
if (targetSum == 0 && root.left == null && root.right == null) {
ans.add(new LinkedList<>(path));
}
dfs(root.left, targetSum);
dfs(root.right, targetSum);
path.remove(path.size()-1);
}
}

Z字形变换

题目的含义就是让你把给定的字符串
按照给定的几行进行Z型输出
总体思路:
1、建立一个字符串的数组array,去保存每一行的字符
2、当向下走的时候每次给index+1,当向上走的时候给index-1
3、当到最底下的时候给index-1
以上2和3步的时候,每次都把字符放到对应的字符串数组中
4、最后把字符串数组拼接到一起(成一个字符串)就是Z型变化后的结果了
以第一个用例举例说明:
s = "PAYPALISHIRING", numRows = 3
1、循环P,将P添加到array[0]中
2、循环A,将A添加到array[1]中
3、循环Y,将Y添加到array[2]中
4、循环P,index要开始-1,然后将P添加到array[1]中
5、循环A,index要开始-1,然后将A添加到array[0]中
6、循环L, index已经到0位置了,所以开始index+1,将L添加到array[1]中
7、直到循环结束
最终有三个字符串分别是:
PAHN
APLSIIG
YIR
最终结果是:PAHNAPLSIIGYIR
public static String convert(String s, int numRows) {
if (s == null || s.length() < 1 || numRows == 1) {
return s;
}
StringBuilder[] array = new StringBuilder[numRows];
for (int i = 0; i < numRows; i++) {
array[i] = new StringBuilder();
}
int dir = 1;
int index = 0;
for (char c : s.toCharArray()) {
array[index].append(c);
index = index + dir;
if (index == numRows - 1 || index == 0) {
dir = -dir;
}
}
StringBuilder res = new StringBuilder();
for (int i = 0; i < numRows; i++) {
res.append(array[i]);
}
return res.toString();
}
public static void main(String[] args) {
String s = "PAYPALISHIRING";
int numRows = 3;
System.out.println(convert(s,numRows));
}

表达式加法和减法

Solo小学一年级的时候做数学题很莫名奇妙,经常把算术表达式加上很多空格(如:7+ 31-2),让老师很是

头大于是老师决定雇用你编写一个程序来独立计算Solo的答案。Can you help theteacher?

解答要求时间限制:5000ms,内存限制:100MB输入输入只有一行,即一个长度不超过100的字符串S,表

示Solo的算术表达式,且S只包含数字和“+”"-"两种运算符,以及Solo加上的一大堆空格(我们保证输入都

是合法的)。注意:5中不一定包含运算符,且我们保证S中不会出现大于100000的数。

输出:表达式的运算结果

/*题目:
给你一个包含空格的字符串,请你计算出+-运算的结果
思路:记录上次的符号(默认为+),和下一次的数字,进行运算
1、先将每个数求出来,记录到num中,
2、上次是+号,将当前的num加到res结果中
3、上次是-号,将res减去num的结果保存起来
4、记录下次的符号,并重置下num的值
5、直到将s字符串循环完为止,res中就是所求的和
举例:7+31 -2
1、先将7保存到num中
2、遇到+号后,将7加到res中(res=7)
3、遇到3后,将3放入num中
4、遇到1后,将3*10+1放入到num中
5、遇到空格直接跳过
6、遇到-号后,将31加到res中(res=38)
7、遇到2后,将2放入到num中(num=0)
8、因为2是最后一个字符了,所以最后一个字符也可以进入到运算中
9、res-2 放入到res中,res=36
循环结束,结果就是36*/
public class Main {
public static int calc(String s) {
int res = 0;
char op = '+';
int num = 0;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (Character.isDigit(ch)) {
num = num * 10 + (ch - '0');
}
if (!Character.isDigit(ch) && ch !=' ' || i == s.length() - 1) {
if (op == '+') {
res += num;
} else if (op == '-') {
res -= num;
}
op = ch;
num = 0;
}
}
return res;
}
public static void main(String[] args) {
String s = "10 - 1 + 25";
System.out.println(calc(s));
}
}

下一个更大元素Ⅲ

/*这个题就是小岛问题
思路:
1、就是去循环二维数组中的所有元素,当遇到1的时候进入递归
2、进入递归后,将上下左右四个方向的为1的改成0
3、直到越界或者遇到0为止
总共判断有多少个小岛就可以了*/
import java.util.*;
public class Main {
public static void main(String[] args) {
/*char[][] grid = {{'1','1','1','1','0'},
{'1','1','0','1','0'},
{'1','1','0','0','0'},
{'0','0','0','0','0'}};*/
char[][] grid = {{'1', '1', '0', '0', '0'},
{'1', '1', '0', '0', '0'},
{'0', '0', '1', '0', '0'},
{'0', '0', '0', '1', '1'}};
System.out.print(numNetworks(grid));
}
public static int numNetworks(char[][] grid) {
int cnt = 0;
for (int i = 0; i < grid.length; ++i) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') { //遍历矩阵 如果遇到1 就将1相邻的四个方向
都改为0 那么进入该if几次
dfs(grid, i, j); //就相当于有几个小岛
cnt++;
}
}
}
return cnt;
}
private static void dfs(char[][] nums, int i, int j) {
if (i < 0 || j < 0 || i >= nums.length || j >= nums[0].length) {
return; //递归可以走4个方向 那么i j下标有可能越界 越界了 就结束当前递归
}
if (nums[i][j] == '0') {
return; //若当前位置值为0 则结束递归 不继续
}
nums[i][j] = '0'; //若能走到这里 代表当前值为1 那么将其翻转为0 防止重复走回头
路
int[][] run = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
//用数组存储四个个相邻方向的差值 比如 0 1 就是向右 0 -1 就是向左 1, 0 就是
向下 1 1 就是右下方
for (int m = 0; m < run.length; ++m) {//循环遍历数组的每一个方向 继续递归
调用传播翻转
dfs(nums, i + run[m][0], j + run[m][1]);
}
}
}

全部评论

相关推荐

#软件开发笔面经#华为od有机考、资格面、心理测评、技术一面、技术二面、主管面。我从三月准备机试,到四月底进行。机考一共三道题,分数分别是100、100、200.按照测试用例进行给分。目标院校需要150+,非目标院校需要300+,作答的语言没有限制,但最好是和后面面试的岗位保持一致。资格面主要询问个人意向情况,一般在半小时以内。心理测评主要考察前后问题答案的一致性。技术一面会问一些八股,然后有一道手撕代码,差不多是力扣简单或者中等难度,偶尔也会遇到困难难度,作答语言同样没有限制。八股的话基本上就是该开发语言一些常规的经典问题,花时间刷一刷背一背,谈一点个人的看法就好。技术二面和技术一面类似。面试官会给到面试人员一个等级,如果两次面试评级不一致,会加试技术终面来确定最后的入职级别。主管面的面试官level很高,会让你做自我介绍,对你的项目简要提问,然后询问你的意向程度,问了个人爱好,还有就是od是和德科或科瑞签合同,与华为正式员工和华为公司签合同是不一样的,问你的看法,还有自己在单位的一个长期发展规划,会问到对加班的看法,最后会问你的意向薪酬。告诉面试官之后,他会让我们了解不同地域的用人成本存在差异性,要注重长期发展。或许我们现在以他人为模范,但他日我们亦或会成为他人的标杆。主管面在半小时以内。华为内部有竞业协议,你将身份证号邮箱姓名简历发给HR后会锁简历,后期只能由该HR和你对接指导你进行后续笔面流程。在经历了机考、资格面、心理测评、技术一面、技术二面、主管面之后的我正在等通知ing
点赞 评论 收藏
分享
2 8 评论
分享
牛客网
牛客企业服务