华为机试 | 笔记

以下为本人在刷题时的笔记,思路及要点仅作为参考,不作为标准的答案。不足之处,欢迎在评论区批评指正~

【第一部分:题库】

1、勾股数元组

1. 判断质数

boolean数组默认true → 0和1不是 → 从i=2开始,对i的倍数从i倍开始,置为false

public static boolean isPrimer(int n) {
	boolean[] countPrime = new boolean[n+1];
	Arrays.fill(countPrime, true);//1. 先默认都是质数
	countPrime[0] = false; countPrime[1] = false;//2. 0-1不是质数
	//3. 如果x是质数,那么2x, 3x, 4x...不是质数
	/*后面的数都默认是质数,从2,3,5,7...开始依次将它们的倍数排除,总能将所有数字遍历过一遍*/
	for(int i = 2; i <= n; i++) {
		if(countPrime[i] && i*i <= n) {
			for(int j = i*i; j <= n; j +=i) {//i的倍数所以每次j+i
				countPrime[j] = false;
			}
		}
	}
	return countPrime[n];
}//isPrimer
2. 两数互质

概念:公因数只有1
判断方法:1)两个质数互质;2)1与任何数互质;3)a的所有质因子与b的所有质因子互质。

	public static boolean isHuzhi(int a, int b) {//是否互质
		if(isPrimer(a) && isPrimer(b)) {//两个质数互质
			return true;
		}else if(a == 1 && b == 1) {//1与任何数互质
			return true;
		}else{//如果没有相同的质因子,则互质
			Set<Integer> set1 = new HashSet<Integer>(), set2 = new HashSet<Integer>();
			set1 = getZhiList(a); set2 = getZhiList(b);
			
			int count = 0;//相同质因子的个数
			for(Integer num : set1) {
				if(set2.contains(num)) count++;
			}
			if(count == 0) return true;
		}//if-else
		return false;
	}//isHuzhi

public static Set<Integer> getZhiList(int n){//Set内的元素不重复
/*因为只需要判断是否有相同的质因子,某个质因子的数量无关,所以可以用Set*/
	Set<Integer> set = new HashSet<Integer>();
	int i = 2;
	while(i <= n) {
		if(isPrimer(i) && (n % i == 0)) {//是质数,且能整除,那就是质因子
			n = n / i;
			set.add(i);
			if(n == 1) break;//已经除到1了
			i = 2;//i又重新从2开始算
		}else {
			i++;
		}
	}//while
	return set;
}//getZhiList

2、数组

如果只需要在一个方向上操作数组,且每次只需要判断第一个数,则用Queue。但这题不是,所以不能用Queue,数组判断边界很重要。

3、遍历矩阵

4、TLV????

认真读题。

1. 小端序

01 00 为 00 01
02 00 为 00 02
03 00 为 00 03

5、猴子爬台阶——1、k步——动态规划

走台阶的dp[]是一维的

  1. dp[i]的含义:从开始到第i个台阶一共有几种走法
  2. 递推:dp[i][j] = dp[i-1][j] + dp[i-3][j]
  3. 初始化:0所以for循环从i=k+1开始。
  4. 遍历顺序:左右,上下
  5. 递推一遍公式 。思路就是这样,注意检查边界条件

6、GPU的并行度

注意检查边界。

7、按差值的绝对值排序

分析好题目即可。

8、字符串句子反转

去掉首位空格。

str = str.trim();

9、购物——动态规划

  1. String[] 转 int[]——lambda表达式
String[] str_arr = sc.nextLine().split(",");
/*个人理解:
2. stream(str_arr):把str_arr数组放入stream流中;
3. mapToInt():把String类型的数字映射成int;
4. Integer::parseInt==还不是很理解,“意会”:Integer.parseInt(str_arr中的每个数字)
5. toArray():转成数组类型。
*/
int[] nums = Arrays.stream(str_arr).mapToInt(Integer::parseInt).toArray();
  1. String[] 转list
List<Integer> list;
list = Arrays.stream(sc.nextLine().split(" ")).map(Integer::parseInt).collect(Collectors.toList());
  1. 动态规划也可以,但是每件商品的价格不一样,dp[][]数组会有很多空位,所以还是暴力for循环。

10、字符统计

11、取模处理

遍历map的value

for(Integer num : map.values()) max = Math.max(max, num);

12、没有题目

13、二叉树的遍历——重写

二叉树的遍历,需要记录路径
用数组存的时候,下标从1开始

14、货车载重

数组排序后计算即可。认真读题

15、题目读不懂

16、单词接龙

不是回溯,是循环

1. String 和 StringBuilder

1)区别
String在重新赋值后,回开辟一个新的空间来存储新的字符串;
StringBuilder不会重新分配内存,而是在原来内存空间的基础上操作;二者在拼接时语法也有差异。
所以,涉及字符串拼接的,最好用StringBuilder

2)转换

s = sb + "";//String 类型很包容,什么加一下""就能变成String
s += "abc";//String对象可以用加号+
sb.append("abc");//StringBuilder对象要用append()方法
2. "环形"操作用while

17、单词统计

头脑清醒就好。

1. 对map按value排序
List<Map.Entry<Character, Integer>> list = new ArrayList<>(map.entrySet());

Collections.sort(list, new Comparator<Map.Entry<Character, Integer>>(){
@Override
public int compare(Entry<Character, Integer> o1, Entry<Character, Integer> o2) {
		// TODO Auto-generated method stub
		return o2.getValue() - o1.getValue();//降序
	}
});

18、围圈喊7

读懂题目是王道

19、删除出现次数最少的字符

使用str.subsring()需注意

str.replace(temp, "");//不改变str的值
str = str.replace(temp, "");//需要赋值才能改变

20、走步数

理解好步长步数

21、通信系统调度A/B/C

如何标记已经使用过的调度是重点。

22、同时操作多组数组

对于List >,当list内的队列为空时,要用list.remove()删掉,否则,list里的所有队列都清空了之后,list.isEmpty()仍为false

23、内存容量排序

  1. 现在可以不用实现归并、快排这些基础的排序算法了,所以要想着怎么转换问题。内存容量的单位有很多,所以比较前要统一单位。
  2. 如果有两个不同的维度,可以同时用数组和map存储。

24、身高、体重排序

需要同时记录次序、身高、体重,所以需要int[n][3]的数字;身高升序,身高相同则按体重升序,即先按第二列排序,再按第三列排序。

1. 二维数组排序
int[][] hw = new int[n][3];
Arrays.sort(hw, new Comparator<int[]>() {
@Override
	public int compare(int[] o1, int[] o2) {//对每个int[1][3]维的数组,
		if(o1[1] == o2[1]) //如果第1维身高相等
			return o1[2] - o2[2];//体重升序排列
		else
			return o1[1] - o2[1];//否则就按照身高递增排列
	}
});

25、无题

26、数字按最小公约数分类

1. String[]、int[]转list

Lambda表达式不太好理解,还是用常规方法把

27、数组排序

取十进制的最低位,就mod 10,负数的最低位也为负数,即-21 % 10 = -1。

28、数组去重排序

29、单词联想

匹配前缀

1. set的输出要记得try catch,避免由于set为空造成的报错。
try {
	for(String word : set) {
		if(word.length() >= pre.length()) {
			if(word.substring(0, pre.length()).equals(pre)) {
				System.out.print(word + " ");
			}
		}
	}//for-set
}catch(Exception e) {
	System.out.println(pre);
}
2. 字典序——TreeSet

30、字符串轮转

可以先想好关键代码怎么写,然后再决定以什么数据类型接收输入。

31、自然数分解成连续自然数之和

想不到取巧的方法那就暴力破解

32、数组滑动窗口和最大值

好好审题即可。

33、内存分配

审题。

34、众数中的中位数

35、数组操作

操作两个数组时,要分析清楚是两层for循环还是双指针移动

36、数组处理

参赛团队的最低能力值为N。

37、作业调度——短作业优先

流水线作业,作业已经安排好了,不是实时的,所以还比较好算。找到数学模型。

38、url前缀后缀拼接——字符串

39、描述上一个数字

动态规划 + 认真分析。

40、分班

需要动点脑子的,真正笔试的时候,可以,先写别的,再回来写这个。

有些题是逻辑复杂,有些题是数据处理繁琐

41、全排列——回溯——记!!

0. 全局变量 path(用栈),list
1. 递归函数的参数
2. 终止条件
3. 单层搜索的逻辑
//1. 回溯函数的参数列表:由于操作的就是1-n的数,所以不需要待处理的数组了
public static void backTracking(int n, int k, boolean[] used) {
	//2. 回溯终止条件
	if(path.size() == n) {//得到了一个排列
		count++;
		if(count == k) {//题目要求输出第k个就好
			for(Integer i : path) System.out.print(i);
			System.out.println();
			return;
		}
		return;
	}
		
	//3. 单层遍历!!!!!!!重点
	for(int i = 1; i <= n; i++) {
		//1. 操作
		if(used[i]) continue;//当前元素已经使用过
		used[i] = true;
		path.push(i);
		//2. 回溯
		backTracking(n, k, used);
		//3. 恢复现场
		used[i] = false;
		path.pop();
	}
}//backtracking

42、VLAN id的分配

1. 字符串包含某个字符
if(str_arr[i].contains('-')) {}//会报错
if(str_arr[i].contains('-' + "")) {}//这么写才对

43、整数编码

44、和为target的连续子序列的最长长度

动态规划

45、数组处理

46、火星文表达式运算

1. 中缀转后缀
  • 数字:直接加到后缀表达式(注意多位数字的情况)
  • 符号:压得住的进栈op_stack,压不住的排他再进;
    后缀的结果要用队列来保存
    for 循环扫描中缀表达式完毕后,注意点:
    1)检查最后一个数字是否被取走
    2)检查num_stack 中是否还有操作符,有的话要加入后缀表达式
2. 计算后缀表达式
  • 1个栈:num_stack
3. 字符串相等要用.equals()

47、单词反转 计算开音节单词

1. 如果字符串对于[只含字母] 和 [含有特殊字符]的字符串有不同的操作的时候,最好是matchs(“[a-z]”)来判断是纯字母,else即为包含其他字符;因为正则的“非”我不会表示。

48、核心:两个单词之间是否相同字符

1. for 循环:第一个单词的所有字母加入set
2. for 循环:依次判断第二个单词里的字母在set中有没有

49、货车载重——同T14

50、小明分糖果

1.1 判断某数是否为2的幂次方
1.2 如果是,那是几次方

方法1:循环统计
方法2:利用二进制的位数——自己想到的
例如:2 = 10(2) 4 = 100(2) 8 = 1000(2),它们的幂次就是二进制字符串长度-1;

int n = 8;
int mici = Integer.toBinaryString(n).length() - 1;

所以:如果是2的幂次方,那么二进制就只有最高位为1;计算是几次方如上所述。

2. 获取里当前数最近的2的幂次方但这个思路在这道题不对

思路:二进制的位数
例如:13 = 1101(2),一共4位二进制,所以需要对比二进制数b1 = 1000(2)和b2 = 10000(2)所对应的十进制与13的差值即可;即计算23 和24与13的差值,然后对比。

int i = 9;
String bi_str = Integer.toBinaryString(i);
				
int a = (int)Math.pow(2, bi_str.length()-1);
int b = (int)Math.pow(2, bi_str.length());
int res = 0;
if(Math.abs(a-i) < Math.abs(b-i)) {
	res = a;
}else {
	res = b;
}
3. 去掉字符串的第一个字符
substring(i):去掉下标i之前的字符,i从0开始
str = str.substring(0);//错
str = str.substring(1);//对-去掉下标1之前的字符,也就是0号字符
  1. 动态规划
/*递推公式*/
dp[i] = 1 + dp[i/2];//i为偶数
dp[i] = Math.min(1 + dp[i-1], 1 + dp[i+1]);//i为奇数
//dp[i]即跟前面相关又跟后面相关,而动态规划只能按一个方向遍历,那动态规划还能用吗?
//转换一下思路:i为奇数时,i+1为偶数,则dp[i+1] = 1 + dp[(i+1)/2];
//所以:
dp[i] = Math.min(1 + dp[i-1], 2 + dp[(i+1) / 2]);//i为奇数

51、非严格递增数字序列

1. replace()和replaceAll()的区别
  • replace()的参数是char或String
  • replaceAll()的参数是正则表达式
2. 递增子序列的dp是几维的?
  • 连续的是二维的,不连续的是一维的。所以做题的时候要审好题,特别是示例。
3. Arrays.fill()

该方法的参数是一维数组,二维数组用不了

审好题,不要先入为主

52、内存排序——同T23

54、句子字符串反转——同T8

55、单词接龙——同T14

56、GPU并行调度——同T6

57、出现次数第k多的字母——同

58、流水线作业调度+短作业优先——同

59、考勤统计

当有很多要求的时候,可以先统计,再判断

60、删掉连续相同字符,并连续操作

用栈

1. 判断字符串是否含有数字、字母以外的符号
if(!str.matches("[A-Za-z]*")) {}//“正”的正则不会写,只能反着来

61、字符串匹配

简易KMPstr1中是否包含全部的str2

62、压缩算法解压缩

若想在字符串中分离多位数字时,可以在for循环内套while

63、字符占用计算

1. 如果说了字典序,那就想到TreeSet 和 TreeMap;如果说要按顺序,那就LinkedList

64、坐标移动计算面积

(x, offsetY):左边是横坐标x的值,右边是y的偏移量,不是y的值,审好题。
如果发现以你的理解和题目给的样例不同,那就推倒自己的理解,试一下别的理解方式。

65、第K个最小ASCII码值的字母

1. 问题

问题:需要按ASCII码排序,第一时间想到的是TreeSet 和 TreeMap,但Set 和 Map不能随意获取第i个值。
解决:用List存储,再对List排序。
但数据结构的变化就会带来操作上的变化,没有十全十美的方法,算法都要考虑用空间换时间呢~

2. 自定义数据类型

66、连续元音字符串——动态规划

连续:只需要一维数组;不连续时才需要二维数组

67、只能交换两个字符,获取最接近ASCII排序的字符串

求交换后的字符串
只能交换两个字符,使得字符串最接近ASCII排序
求最小交换次数

1. char[]
char[] chs = str.toCharArray();
char[] temp = chs;//这样赋值的话,chs改变temp也会边;所以,应该如下:
char[] temp = str.toCharArray();
2. 思路

for 从第一个字符到最后一个字符,每次循环都找到后面第一个比自己小的,然后交换。

68、和的绝对值最小的两个数

如果没有取巧的方法,那就for 循环遍历。

70、水仙花数

for 循环暴力

71.高-矮-高-矮最小移动距离

思路
for 循环扫描一遍数组,下标0开始,数据类型最好用list而不用数组
偶数位(0, 2…):高-矮才合规,不合规则与后面的换位置
奇数位(1, 3…):矮-高才合规,不合规好与后面的换位置

72、下一个比我大的高的同学的位置

动态规划

73、字符串统计

大小写分开

74、停车场中最少的车辆数

思路清晰,认真分析。

75、连续最长相同字符

动态规划dp[] 一维

76、下一个比我大的高的同学的位置——同72

77、双十一小明购物——同

78、时间排序

二维数组排序。

79、身高体重排序

审好题,递增or递减看清楚即可。

80、n个数全排列的第k个——同

81、出租车计费表

动态规划–变形

1. 先正向求结果,再反向求下标

反向求下标可以优化:每求得一个dp[i]就判断一下下标,如果下标等于n,那就输出。

2. for循环同时控制两个变量
for(int i = weishu - 1, mici = 0; i >= 0; i--, mici++) {
	String num_str = next + "";
	if(num_str.charAt(i) == '4') {
		next += Math.pow(10, mici);
	}
}//每一位

82、字符串中整数最小和

84、多个多位数组成最大整数

1. list.sort() 详解

1)return
return > 0:递增;return < 0:递减;return = 0:不变。

  • return o1 - o2;——递增
  • return o2 - o1;——递减

2)o1, o2
o1:后一个数
o2:前一个数

2. 思路

先按首位排序收集,再排序

85、简易剪切板

5:选择当前屏幕上的所有字母了之后,屏幕上就没有字母了
题目一定要理解好

86、成绩排序

一个人有多个成绩,乱序输入,如果某同学的成绩少于3次,则不参与排名,否则按照每人成绩最高3个的总和降序排列。成绩相同则按id降序。
成绩统计:一般都需要用到多次用到Map,List

============================================================================

【第二部分:机试攻略】


一、必会题

1、字符串分割

18、分积木

1. 核心运算,异或:异为1——加法遇1不进位——还真的有异或这个运算符。
int a = 0;
int b = 1;
System.out.println(a ^ a);//0
System.out.println(b ^ b);//0
System.out.println(a ^ b);//1
2. 技巧:总的积木重量异或为0

因为哥哥弟弟的两堆积木,异或的结果相等,而这两个结果又异或,结果为0。所以,如果积木总重量异或不为0,那说明不能按题目要求分成两堆。故,需要在分成两堆之前先判断是否能分成两堆。

3. 思路

判断能分成两堆后,遍历一遍数组,找到数组中的最小值min,和所有值的总和sum,sum - min 即为哥哥能分到的最大重量的积木。==但为什么是sum - min,原理还不是很理解。

23、贪吃蛇

  • 要每操作一次都要判断是否结束了。
  • 审好题。

24、解密犯罪事件

  • 先看能不能构成今天的“假事件”之后的时刻,不能再构造第二天的。

28、二叉树的中序遍历

33、简易内存池

好好审题

37、数组二叉树

数组需要重新处理一下,使得数据从下标为1开始存储。

38、考古学家

42、火锅

审好题。理解好,自己手动模拟。然后再把步骤写出来

66、二叉树的广度优点遍历

二叉树先构造,再按要求遍历

68、招聘

查看区间是否重叠,不重叠就可以可以简单运算

69、斗地主之顺子

  • 不难

二、参考题

1、剑指offer 62 题: 圆圈中最后剩下的数字

想实现循环链表的功能,但是太复杂了(对现在的我来说),所以用while手动模拟实现了循环链表。

2、无最长重复字符的子串——滑窗

有固定窗口和不固定窗口
符合:end++——拓展窗口
不符:start++——更换窗口起点

3、LeetCode14:最长公共前缀

判断第i为单词是否相等:将每个单词的第i个字符加入set,看最后set的大小,为1则相同,不为1则不同。

4、LeetCode15 翻转字符串里的词

5、LeetCode2047 合法字符的个数

1. 正则表达式

1)多个空格

String[] strs = str.trim().split("\\s+");//多个空格

2)数字、字母

//是否全是
if(str.matches("[0-9]+")) {} //是否全是数字
if(str.matches("[A-Za-z]+")) {} //是否全是字母
if(str.matches("[A-Za-z0-9]+")) {} //是否全是字母、数字
//是否包含
if(str.matches(".*[0-9].*")) {} //是否包含数字
if(str.matches(".*[A-Za-z].*")) {} //是否包含字母

3)如何判断是否包含连字符-

if(str.contains("-")) {
	String temp = str.replaceAll("-", "");
	System.out.println(str.length() + temp.length());//包含几个连字符
}

【第三部分:代码随想录】

一、树的遍历

1、LC_144树的遍历

1. 将一个list拼接到另一个list中
list.addAll(list1);
2. 返回空的list
//1. 错误
return null;
//正确
List<Integer> res = new ArrayList<Integer>();
if(root == null) return res;//此时list为空
3. 前序遍历
public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<Integer>();
    if(root == null) return res;
    	
    res.add(root.val);
    	
    List<Integer> left_list = preorderTraversal(root.left);
    if(left_list != null) res.addAll(left_list);

    List<Integer> right_list = preorderTraversal(root.right);
    if(right_list != null) res.addAll(right_list);
    	
    return res;
}//前序
	
public class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode() {}
	TreeNode(int val) { this.val = val; }
	TreeNode(int val, TreeNode left, TreeNode right) {
		this.val = val;
		this.left = left;
		this.right = right;
	}
}//class-Node

你可能感兴趣的:(算法,Coding,经验分享,java)