css
1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自适应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解
js
1,判断 js 类型的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 事件有哪些阶段?谈谈对事件代理的理解
7,js 执行机制、事件循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾回收机制讲一下,
14,对前端性能优化有什么了解?一般都通过那几个方面去优化的?
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
}
## 4. 有效的字母异位词
题目描述:给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词。
注意:若s和t中每个字符出现的次数都相同,则称s和t互为字母异位词。
### 4.1解法一:(这题跟第2题的解法有些相似)
class Solution {
public boolean isAnagram(String s, String t) {
// 如果两个字符串的长度不同,它们不可能是字母异位词
if (s.length() != t.length()) {
return false;
}
// 创建一个长度为 26 的数组来记录每个小写字母出现的次数
int[] counts = new int[26];
// 遍历字符串 s,增加每个字母的计数
for (char ch : s.toCharArray()) {
// 将字符转换为小写,并增加对应字母的计数
counts[ch - 'a']++;
}
// 遍历字符串 t,减少每个字母的计数
for (char ch : t.toCharArray()) {
// 将字符转换为小写,并减少对应字母的计数
counts[ch - 'a']--;
// 如果计数小于 0,说明 t 中包含的某个字母比 s 中多,不是字母异位词
if (counts[ch - 'a'] < 0) {
return false;
}
}
// 如果以上两个循环都没有返回 false,说明 t 是 s 的字母异位词
return true;
}
}
## 5.重复的子字符串
题目描述:给定一个非空的字符串s,检查是否可以通过由它的一个子串重复多次构成。
### 5.1.解法一:
// 定义一个名为Solution的类
class Solution {
// 定义一个公共方法repeatedSubstringPattern,接收一个字符串s作为参数
// 返回值为boolean类型,表示字符串s是否可以通过重复其子字符串来形成
public boolean repeatedSubstringPattern(String s) {
// 将字符串s拼接成两倍长度的字符串str
String str = s + s;
// 检查拼接后的字符串(除了首尾字符)是否包含原字符串s
// 如果包含,说明原字符串s可以通过重复某个子串来形成
// 注意:substring(1, str.length() - 1)排除了首尾字符,是为了避免匹配到完整的s+s
return str.substring(1, str.length() - 1).contains(s);
}
}
## 6.移动零
给定一个数组nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。
**请注意**,必须在不复制数组的情况下原地对数组进行操作。
### 6.1解法一:
class Solution {
public void moveZeroes(int[] nums) {
// 外层循环遍历数组
for(int j=0; j<nums.length; j++){
// 内层循环从数组开头开始,检查每个元素是否为0
for(int i=0; i<nums.length-1; i++){
// 如果当前元素为0
if(nums[i]==0){
// 交换当前元素和下一个元素
int tmp = nums[i+1];
nums[i+1] = nums[i];
nums[i] = tmp;
}
}
}
// 这里存在一个问题:方法的时间复杂度为O(n^2),并且只能处理相邻的0和非0元素。
// 更好的方法是一次遍历数组,将所有非零元素移到前面,同时记录0的个数,
// 然后将剩余的位置用0填充。这样可以将时间复杂度降低到O(n)。
}
}
### 6.2解法二:
class Solution {
public void moveZeroes(int[] nums) {
int count = 0; // 记录0的个数
// 遍历数组,将所有非零元素移到前面
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[count] = nums[i]; // 将非零元素放到正确的位置
count++; // 增加非零元素的计数
}
}
// 将剩余的位置用0填充
while (count < nums.length) {
nums[count] = 0;
count++;
}
}
}
## 7.加一
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位,数组中每个元素只存储单个数字。
你可以假设除了整数0以外,这个整数不会以零开头。
### 7.1解法一:
// 定义一个Solution类
class Solution {
// 定义plusOne方法,输入一个整数数组digits,输出加1后的整数数组
public int[] plusOne(int[] digits) {
// 从数组的最后一位开始遍历,因为加1操作是从最低位开始的
for(int i = digits.length-1; i >= 0; i--){
// 将当前位加1
digits[i]++;
// 对当前位取模10,确保每位的值在0-9之间
digits[i] = digits[i] % 10;
// 如果当前位不为0,说明没有发生进位,直接返回加1后的数组
if(digits[i] != 0) return digits;
}
// 如果循环结束还没有返回,说明原数组的所有位都是9,发生了进位
// 创建一个比原数组长度多1的新数组,用于存放进位
digits = new int[digits.length + 1];
// 将新数组的最高位(索引为0的位置)设为1,表示进位
digits[0] = 1;
// 返回新数组
return digits;
}
}
作者:YHHZW
链接:https://leetcode.cn/problems/plus-one/solutions/4481/java-shu-xue-jie-ti-by-yhhzw/
来源:力扣(LeetCode)
### 7.2解法二:
class Solution {
public int[] plusOne(int[] digits) {
int n = digits.length;
for (int i = n - 1; i >= 0; --i) {
if (digits[i] != 9) {
++digits[i];
for (int j = i + 1; j < n; ++j) {
digits[j] = 0;
}
return digits;
}
}
// digits 中所有的元素均为 9
int[] ans = new int[n + 1];
ans[0] = 1;
return ans;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/plus-one/solutions/1057162/jia-yi-by-leetcode-solution-2hor/
来源:力扣(LeetCode)
### 73解法三:(根据解法二改编)
class Solution {
public int[] plusOne(int[] digits) {
for(int i = digits.length-1;i>=0;i–){
if(digits[i] != 9){
digits[i]++;
return digits;
}else {
digits[i] = 0;
}
}
int[] ans = new int[digits.length+1];
ans[0] = 1;
return ans;
}
}
## 8.数组元素积的符号
题目描述:已知函数signFunc(x)将根据x的正负返回特定值:
如果x是正数,返回1
如果x是负数,返回-1
如果x等于0,返回0
给你一个整数数组nums。令product为数组nums中所有元素值的乘积
返回signFunc(product)
### 8.1解法一:
class Solution {
public int arraySign(int[] nums) {
int negativesCount = 0;
for (int num : nums) {
if (num < 0) {
negativesCount++;
} else ifa (num == 0) {
return 0; // 如果数组中有零,则直接返回零
}
}
// 如果负数的数量是偶数,则返回1;如果是奇数,则返回-1
return negativesCount % 2 == 0 ? 1 : -1;
}
}
### 8.2解法二:
// Solution类包含了arraySign方法
class Solution {
// arraySign方法接受一个整数数组nums作为参数
public int arraySign(int[] nums) {
// 初始化一个变量sign,用于记录乘积的符号,初始值为1(表示正数)
int sign = 1;
// 遍历数组nums
for(int i = 0; i <= nums.length - 1; i++){
// 如果当前元素是0,则直接返回0,因为任何数与0相乘都是0
if(nums[i] == 0)
return 0;
// 如果当前元素是负数,则改变乘积的符号(由正变负或由负变正)
if(nums[i] < 0)
sign = -sign;
// 循环结束后,返回最终的符号值
}
return sign;
}
}
## 9. 判断能否形成等差数列
给你一个数字数组arr
如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为等差数列
如果可以重新排列数组形成等差数列,请返回true;否则,返回false
### 9.1解法一:
import java.util.Arrays;
class Solution {
public boolean canMakeArithmeticProgression(int[] arr) {
// 对数组进行排序
Arrays.sort(arr);
// 检查排序后的数组是否形成等差数列
int d = arr[1] - arr[0]; // 计算第一个公差
for (int i = 2; i < arr.length; i++) {
if (arr[i] - arr[i - 1] != d) {
// 如果发现公差不一致,尝试使用负公差重新检查
d = -(arr[i] - arr[i - 1]);
for (int j = 1; j < i; j++) {
if (arr[j] - arr[j - 1] != d) {
// 如果不是所有相邻元素的差都等于新的公差,则不是等差数列
return false;
}
}
// 如果所有相邻元素的差都等于新的公差,则是等差数列
return true;
}
}
// 所有相邻元素的差都等于第一个公差,是等差数列
return true;
}
}
## 10.单调数列
如果数组是单调递增或单调递减的,那么它是单调的
如果对于所有i<=j,nums[i]<=nums[j],那么数组nums是单调递增的
如果对于所有i<=j,nums[i]>=nums[j],那么数组nums是单调递减的
当给定的数组nums是单调数组时返回true,否则返回false
### 10.1解法一:
class Solution {
public boolean isMonotonic(int[] nums) {
boolean increasing = true;
boolean decreasing = true;
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i - 1]) {
increasing = false;
}
if (nums[i] > nums[i - 1]) {
decreasing = false;
}
}
// 数组要么是递增的,要么是递减的
return increasing || decreasing;
}
}
## 11.罗马数字转整数
罗马数字包含以下七种字符: `I`, `V`, `X`, `L`,`C`,`D` 和 `M`。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 `2` 写做 `II` ,即为两个并列的 1 。`12` 写做 `XII` ,即为 `X` + `II` 。 `27` 写做 `XXVII`, 即为 `XX` + `V` + `II` 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 `IIII`,而是 `IV`。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 `IX`。这个特殊的规则只适用于以下六种情况:
* `I` 可以放在 `V` (5) 和 `X` (10) 的左边,来表示 4 和 9。
* `X` 可以放在 `L` (50) 和 `C` (100) 的左边,来表示 40 和 90。
* `C` 可以放在 `D` (500) 和 `M` (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。
### 11.1解法一:
class Solution {
// 罗马数字到整数的转换方法
public int romanToInt(String s) {
// 初始化罗马数字对应的整数值的计数器
int Icount=0; // 'I' 的计数器
int Vcount=0; // 'V' 的计数器
int Xcount=0; // 'X' 的计数器
int Lcount=0; // 'L' 的计数器
int Ccount=0; // 'C' 的计数器
int Dcount=0; // 'D' 的计数器
int Mcount=0; // 'M' 的计数器
// 用于暂存某些特殊组合(如 IV, IX 等)转换成的整数值
int number = 0;
// 遍历罗马数字字符串
for(int i=0; i<s.length(); i++){
// 根据当前字符进行不同的处理
switch (s.charAt(i)){
case 'I':
// 如果下一个字符是 'V',则当前 'I' 表示 4,并将索引前进一位
if(i+1<= s.length()-1 && 'V'==s.charAt(i+1) ){
number += 4;
i++; // 注意:这里手动增加了索引,意味着下一次循环会跳过下一个字符
}
// 如果下一个字符是 'X',则当前 'I' 表示 9,并将索引前进一位
else if(i+1<= s.length()-1 && 'X'==s.charAt(i+1) )
{
number += 9;
i++; // 同样手动增加索引
}
// 否则,'I' 表示 1
else
Icount++;
break;
// 其他罗马数字字符的处理类似...
case 'V': Vcount++; break; // 'V' 表示 5
case 'X':
if(i+1<= s.length()-1 && 'L'==s.charAt(i+1) ){
number += 40;
i++;
}
else if(i+1<= s.length()-1 && 'C'==s.charAt(i+1) ) {
number += 90;
i++;
}
else
Xcount++; // 'X' 表示 10
break;
case 'L': Lcount++; break; // 'L' 表示 50
case 'C':
if(i+1<= s.length()-1 && 'D'==s.charAt(i+1)) {
number += 400;
i++;
}
else if(i+1<= s.length()-1 && 'M'==s.charAt(i+1) ) {
number += 900;
i++;
}
else
Ccount++; // 'C' 表示 100
break;
case 'D': Dcount++; break; // 'D' 表示 500
case 'M': Mcount++; break; // 'M' 表示 1000
}
}
// 将所有罗马数字字符的计数转换成整数,并加上特殊组合转换的整数值
return Icount*1 + Vcount*5 + Xcount*10 + Lcount*50 + Ccount*100 + Dcount*500 + Mcount*1000 + number;
}
}
## 12.最后一个单词的长度
题目描述:给你一个字符串s,由若干单词组成,单词前后用一些空格字符隔开,返回字符串中最后一个单词的长度。
单词是指仅由字母组成,不包含任何空格字符的最大子字符串。
### 12.1解法一:
class Solution {
// 计算给定字符串s中最后一个单词的长度
public int lengthOfLastWord(String s) {
// 去除字符串s两端的空格,并找到去除空格后字符串中最后一个空格的位置
int last = s.trim().lastIndexOf(' ');
// 返回最后一个单词的长度
// 这是通过计算去除空格后字符串的总长度减去最后一个空格的位置再减1得到的
// 如果字符串s为空或者只包含空格,则lastIndexOf会返回-1,此时计算得到的长度将是0
return s.trim().length() - 1 - last;
}
}
### 12.2解法二:
class Solution {
public int lengthOfLastWord(String s) {
// 使用空格分割字符串s,得到一个包含所有单词的数组
String[] words = s.split(" ");
// 如果数组为空(即字符串s为空或只包含空格),则最后一个单词的长度为0
if (words.length == 0) {
return 0;
}
// 返回最后一个单词的长度
return words[words.length - 1].length();
}
}
## 13. 转换成小写字母
题目描述:给你一个字符串s,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串
### 13.1解法一:
class Solution {
public String toLowerCase(String s) {
return s.toLowerCase();
}
}
### 13.2解法二:
// 定义一个名为Solution的类
class Solution {
// 定义一个公开的方法toLowerCase,它接受一个字符串s作为参数,并返回转换后的字符串
public String toLowerCase(String s) {
// 创建一个字符数组s1,其长度与输入字符串s相同,用于存储转换后的小写字符
char[] s1 = new char[s.length()];
// 初始化一个整数i,用于遍历字符数组s1
int i = 0;
// 使用增强型for循环遍历输入字符串s中的每个字符
for (char ch : s.toCharArray()) {
// 检查当前字符ch是否是大写字母(ASCII码值在'A'和'Z'之间)
if (ch >= 'A' && ch <= 'Z') {
// 如果是大写字母,则将其转换为小写字母(ASCII码值加32)并存储在s1数组的当前位置
s1[i] = (char)(ch + 32);
} else {
// 如果不是大写字母(可能是小写字母或非字母字符),则直接将其存储在s1数组的当前位置
s1[i] = ch;
}
// 递增索引i,以便在下一次循环迭代中将下一个字符存储在s1数组的下一个位置
i++;
}
// 使用字符数组s1创建一个新的字符串,并返回它
// 这个新字符串包含了原始字符串s中所有大写字母的小写形式
return new String(s1);
}
}
## 14.棒球比赛
你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。
比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表ops,其中 ops[i] 是你需要记录的第 i 项操作,ops遵循下述规则:
1. 整数 `x` - 表示本回合新获得分数 `x`
2. `"+"` - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
3. `"D"` - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
4. `"C"` - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
请你返回记录中所有得分的总和。
### 14.1解法一:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
class Solution {
// 计算得分的方法
public int calPoints(String[] operations) {
// 创建一个ArrayList来存储得分
List list = new ArrayList<>();
// 索引i用来追踪list中当前要操作的位置
int i = 0;
// 遍历operations数组中的每个操作
for(int j=0; j<operations.length; j++){
// 根据操作类型执行相应的动作
switch (operations[j]){
// "+"操作:将当前元素和前一个元素的和添加到list的当前位置
case "+":
// 如果list中至少有两个元素,则进行加法操作
if (i >= 2) {
list.add(i, list.get(i-1) + list.get(i-2));
} else {
// 否则,直接添加当前元素(应该是一个数字)
list.add(i, Integer.valueOf(operations[j]));
}
// 更新索引i
i++;
break;
// "C"操作:撤销上一步的操作,即删除list中的最后一个元素
case "C":
// 如果list不为空,则删除最后一个元素
if (!list.isEmpty()) {
list.remove(--i); // 先减少i的值,再删除元素
}
break;
// "D"操作:将当前元素翻倍后添加到list的当前位置
case "D":
// 如果list至少有一个元素,则进行翻倍操作
if (!list.isEmpty()) {
list.add(i, list.get(i-1) * 2);
} else {
// 否则,直接添加当前元素(应该是一个数字)
list.add(i, Integer.valueOf(operations[j]));
}
// 更新索引i
i++;
break;
// 默认操作:将当前元素(应该是一个数字)添加到list的当前位置
default:
list.add(i, Integer.valueOf(operations[j]));
// 更新索引i
i++;
break;
}
}
// 使用Iterator遍历list并计算所有元素的和
Iterator<Integer> it = list.iterator();
Integer score = 0;
while(it.hasNext()){
score += (Integer)it.next();
}
// 返回得分
return score;
}
}
## 15.机器人能否返回原点
在二维平面上,有一个机器人从原点 **`(0, 0)`** 开始。给出它的移动顺序,判断这个机器人在完成移动后是否在**`(0, 0)` 处结束**。
移动顺序由字符串 **`moves`** 表示。字符 **`move[i]`**表示其第 **`i`**次移动。机器人的有效动作有 **`R`(右),`L`(左),`U`(上)和 `D`(下)。**
如果机器人在完成所有动作后返回原点,则返回 **`true`**。否则,返回 **`false`**。
**注意:**机器人“面朝”的方向无关紧要。 `“**R**”` 将始终使机器人向右移动一次,`“**L**”` 将始终向左移动等。此外,假设每次移动机器人的移动幅度相同。
### 15.1解法一:
class Solution {
// 方法:判断移动是否回到原点
public boolean judgeCircle(String moves) {
// 初始化X轴和Y轴的坐标为(0,0),即起点
int axisX = 0; // X轴坐标
int axisY = 0; // Y轴坐标
// 遍历moves字符串中的每个字符
for (char ch : moves.toCharArray()) {
// 根据字符判断移动方向,并更新坐标
switch (ch) {
case 'U': // 向上移动,Y轴坐标增加
axisY++;
break;
case 'D': // 向下移动,Y轴坐标减少
axisY--;
break;
case 'L': // 向左移动,X轴坐标减少
axisX--;
break;
case 'R': // 向右移动,X轴坐标增加
axisX++;
break;
}
}
// 如果最终X轴和Y轴的坐标都是0,则返回true,表示回到了起点
// 否则返回false,表示没有回到起点
if (axisX == 0 && axisY == 0)
return true;
else
return false;
}
}
## 16.找出井字棋的获胜者
*题目描述:*
*A* 和 *B* 在一个 *3* x *3* 的网格上玩井字棋。
井字棋游戏的规则如下:
* 玩家轮流将棋子放在空方格 (" ") 上。
* 第一个玩家 A 总是用 "X" 作为棋子,而第二个玩家 B 总是用 "O" 作为棋子。
* "X" 和 "O" 只能放在空方格中,而不能放在已经被占用的方格上。
* 只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
* 如果所有方块都放满棋子(不为空),游戏也会结束。
* 游戏结束后,棋子无法再进行任何移动。
给你一个数组 `moves`,其中每个元素是大小为 `2` 的另一个数组(元素分别对应网格的行和列),它按照 *A* 和 *B* 的行动顺序(先 *A* 后 *B*)记录了两人各自的棋子位置。
如果游戏存在获胜者(*A* 或 *B*),就返回该游戏的获胜者;如果游戏以平局结束,则返回 "Draw";如果仍会有行动(游戏未结束),则返回 "Pending"。
你可以假设 `moves` 都 **有效**(遵循井字棋规则),网格最初是空的,*A* 将先行动。
### 16.1解法一:
// 类名:Solution
class Solution {
// 方法名:tictactoe
// 参数:moves,一个二维数组,表示每一步棋的位置,其中moves[i][0]和moves[i][1]分别表示第i步棋的行和列。
// 返回值:一个字符串,表示游戏的胜负情况或状态。
public String tictactoe(int[][] moves) {
// 初始化一个3x3的二维字符数组,表示井字棋的棋盘。
char[][] game = new char[3][3];
// 初始化一个字符变量,表示当前下棋的玩家,初始为’A’。
char step = ‘A’;
// 遍历moves数组,根据每一步的位置在棋盘上放置棋子。
for(int i=0;i<moves.length;i++){
// 如果当前位置没有被玩家'B'(即'O')占据,并且当前玩家是'A',则在该位置放置'X'。
if( game[moves[i][0]][moves[i][1]] != 'O' && step=='A' )
{
game[moves[i][0]][moves[i][1]] = 'X';
step='B'; // 切换到下一个玩家'B'。
}
// 如果当前位置没有被玩家'A'(即'X')占据,并且当前玩家是'B',则在该位置放置'O'。
else if(game[moves[i][0]][moves[i][1]] != 'X' && step=='B')
{
game[moves[i][0]][moves[i][1]] = 'O';
step='A'; // 切换到下一个玩家'A'。
}
}
// 检查第一种胜利情况:对角线(从左上到右下)。
if(game[0][2] == game[2][0] && game[1][1] == game[2][0]){
// 如果对角线上的棋子都是'X',则玩家'A'胜利。
if(game[0][2] == 'X')
return "A";
// 如果对角线上的棋子都是'O',则玩家'B'胜利。
else if(game[0][2] == 'O')
return "B";
}
// 检查第二种胜利情况:对角线(从右上到左下)。
if(game[0][0] == game[1][1] && game[1][1] == game[2][2]){
// 如果对角线上的棋子都是'X',则玩家'A'胜利。
if(game[0][0] == 'X')
return "A";
// 如果对角线上的棋子都是'O',则玩家'B'胜利。
else if(game[0][0] == 'O')
return "B";
}
// 检查行和列是否有连续的三个相同棋子。
for(int i =0;i<3;i++){
int countXA = 0; // 玩家'A'在当前行的'X'的数量。
int countXB = 0; // 玩家'B'在当前行的'O'的数量。
int countYA = 0; // 玩家'A'在当前列的'X'的数量。
int countYB = 0; // 玩家'B'在当前列的'O'的数量。
for(int j=0;j<3;j++){
// 统计当前行和列上'X'和'O'的数量。
if(game[i][j] == 'X')
countXA++;
if(game[i][j] == 'O')
countXB++;
if(game[j][i] == 'X')
countYA++;
if(game[j][i] == 'O')
countYB++;
// 如果某一行或列上有三个连续的'X',则玩家'A'胜利。
if(countXA==3 || countYA==3)
return "A";
// 如果某一行或列上有三个连续的'O',则玩家'B'胜利。
if(countXB==3 || countYB==3)
return "B";
}
}
// 如果所有棋子都已经下完(即moves.length == 9),则游戏平局。
if(moves.length == 9)
return "Draw";
// 如果游戏还没有结束(即moves.length < 9),则游戏仍在进行中。
else
return "Pending";
}
}
## 17.困于环中的机器人
*题目描述:*
在无限的平面上,机器人最初位于 **`(0, 0)`**处,面朝北方。注意:
* **北方向** 是y轴的正方向。
* **南方向** 是y轴的负方向。
* **东方向** 是x轴的正方向。
* **西方向** 是x轴的负方向。
机器人可以接受下列三条指令之一:
* **`"G"`**:直走 1 个单位
* **`"L"`**:左转 90 度
* `"**R"**`:右转 90 度
机器人按顺序执行指令 **`instructions`**,并一直重复它们。
只有在平面中存在环使得机器人永远无法离开时,返回 **`true`**。否则,返回 **`false`**。
### 17.1解法一:
class Solution {
public boolean isRobotBounded(String instructions) {
// 初始化机器人的位置为原点
int axisX = 0; // X轴坐标
int axisY = 0; // Y轴坐标
// 定义方向的数组,0代表向东,1代表向北,2代表向西,3代表向南
String[] directions = {"E", "N", "W", "S"};
// 当前方向索引,初始化为0,即向东
int dirIndex = 0;
// 遍历指令数组
for (char instruction : instructions.toCharArray()) {
switch (instruction) {
case 'G':
// 前进指令
// 根据当前方向移动机器人
switch (dirIndex) {
case 0: axisX++; break; // 向东移动
case 1: axisY++; break; // 向北移动
case 2: axisX--; break; // 向西移动
case 3: axisY--; break; // 向南移动
}
break;
case 'L':
// 左转指令
// 左转90度,即当前方向索引减1,若小于0则回到最后一个方向
dirIndex = (dirIndex + 3) % 4;
break;
case 'R':
// 右转指令
// 右转90度,即当前方向索引加1,若超过3则回到第一个方向
dirIndex = (dirIndex + 1) % 4;
break;
}
}
// 判断机器人是否回到原点或者至少旋转了一次
// 如果回到原点或者至少旋转了一次,则认为机器人是有界的
return (axisX == 0 && axisY == 0) || dirIndex != 0;
}
}
### 17.2解法二:
class Solution {
public boolean isRobotBounded(String instructions) {
// 初始化机器人在坐标轴上的位置,原点为(0, 0)
int axisX = 0; // X轴坐标
int axisY = 0; // Y轴坐标
// 定义所有可能的移动方向,分别对应北、东、南、西四个方向
String[] allDirection = {"axisY","-axisX","-axisY","axisX"};
// 初始化当前移动方向为北方向
String direction = "axisY";
int i = 0;
// 遍历指令字符串中的每个字符
for (char ch : instructions.toCharArray()) {
switch (ch) {
// 左转指令
case 'L':
// 更新方向索引,若超出范围则回到数组开头
if (++i > allDirection.length - 1) i = 0;
// 设置新的方向
direction = allDirection[i];
break;
// 右转指令
case 'R':
// 更新方向索引,若小于0则回到数组末尾
if (--i < 0) i = allDirection.length - 1;
// 设置新的方向
direction = allDirection[i];
break;
// 前进指令
case 'G':
// 根据当前方向更新机器人的位置
switch (direction) {
// 如果当前方向是北,则Y轴坐标加1
case "axisY":
axisY++;
break;
// 如果当前方向是西,则X轴坐标减1
case "-axisX":
axisX--;
break;
// 如果当前方向是南,则Y轴坐标减1
case "-axisY":
axisY--;
break;
// 如果当前方向是东,则X轴坐标加1
case "axisX":
axisX++;
break;
}
break;
}
}
// 判断机器人是否回到原点或方向发生了改变
return (axisX == 0 && axisY == 0) || !direction.equals("axisY");
}
}
## 18.最富有客户的资产总量
题目描述:
给你一个 **`m x n`**的整数网格**`accounts`**,其中 **`accounts[i][j]`** 是第**`i`**位客户在第 **`j`**家银行托管的资产数量。返回最富有客户所拥有的 **资产总量** 。
客户的 **资产总量** 就是他们在各家银行托管的资产数量之和。最富有客户就是 **资产总量** 最大的客户。
### 18.1解法一:
// 定义一个名为Solution的类
class Solution {
// 定义一个公共方法maximumWealth,该方法接收一个二维整数数组accounts作为参数
public int maximumWealth(int[][] accounts) {
// 初始化一个变量maxResource,用于存储最大的财富值,初始值为0
int maxResource = 0;
// 使用外层循环遍历accounts数组中的每个账户
for(int i=0; i<accounts.length; i++){
// 初始化一个变量resource,用于存储当前账户的财富总和,初始值为0
int resource = 0;
// 使用内层循环遍历当前账户中的每个元素(即每个存款)
for(int j=0; j<accounts[i].length; j++){
// 将当前存款累加到resource中
resource += accounts[i][j];
}
// 如果当前账户的财富总和resource大于maxResource,则更新maxResource的值
if(resource > maxResource)
maxResource = resource;
}
// 返回最大的财富值
return maxResource;
}
}
## 19.矩阵对角线元素的和
题目描述:
给你一个正方形矩阵 `mat`,请你返回矩阵对角线元素的和。
请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。
### 19.1解法一:
// 定义一个名为Solution的类
class Solution {
// 定义一个公共方法diagonalSum,该方法接收一个二维整数数组mat作为参数
public int diagonalSum(int[][] mat) {
// 初始化变量primaryDiagonal,用于存储主对角线上的元素和
int primaryDiagonal = 0;
// 初始化变量secondaryDiagonal,用于存储副对角线上的元素和
int secondaryDiagonal = 0;
// 使用两个嵌套的for循环遍历二维数组mat中的每个元素
for(int i = 0; i < mat.length; i++){
for(int j = 0; j < mat[i].length; j++){
// 如果当前元素的行索引i等于列索引j,则它是主对角线上的元素
if(i == j){
// 将主对角线上的元素累加到primaryDiagonal中
primaryDiagonal += mat[i][j];
}
// 如果当前元素的行索引i加上矩阵长度减1等于列索引j,则它是副对角线上的元素
if(mat.length - 1 - i == j){
// 将副对角线上的元素累加到secondaryDiagonal中
secondaryDiagonal += mat[i][j];
}
}
}
// 检查矩阵的长度是否为偶数
if(mat.length % 2 == 0){
// 如果矩阵长度是偶数,则直接返回主对角线和副对角线的元素和
return primaryDiagonal + secondaryDiagonal;
} else {
// 如果矩阵长度是奇数,则需要从结果中减去位于矩阵中心的元素(只计算一次)
// 因为在累加过程中,该元素被计算了两次(一次在主对角线上,一次在副对角线上)
return primaryDiagonal + secondaryDiagonal - mat[mat.length / 2][mat.length / 2];
}
}
}
### 19.2解法二:
// 定义一个名为Solution的类
class Solution {
// 定义一个公共方法diagonalSum,该方法接收一个二维整数数组mat作为参数,并返回对角线上元素的和
public int diagonalSum(int[][] mat) {
// 初始化变量diagonalSum,用于累加对角线上的元素值
int diagonalSum = 0;
// 外层循环遍历二维数组mat的每一行
for(int i = 0; i < mat.length; i++){
// 内层循环遍历当前行的每一列
for(int j = 0; j < mat[i].length; j++){
// 判断当前元素是否位于主对角线上(即行索引i等于列索引j)
if(i == j){
// 如果是,将当前元素值加到diagonalSum上
diagonalSum += mat[i][j];
}
// 判断当前元素是否位于副对角线上(即行索引i与列索引j之和等于矩阵的边长减1)
// 注意这里使用mat.length - 1 - i,因为副对角线从矩阵的右上角开始,行索引递减
else if(mat.length - 1 - i == j){
// 如果是,同样将当前元素值加到diagonalSum上
diagonalSum += mat[i][j];
}
}
}
// 返回对角线上元素的总和
return diagonalSum;
}
}
## 20. 螺旋矩阵
题目描述:
给你一个 **`m`** 行 **`n`** 列的矩阵 **`matrix`**,请按照 **顺时针螺旋顺序** ,返回矩阵中的所有元素。
### 20.1解法一:
import java.util.ArrayList;
import java.util.List;
// 定义一个名为Solution的类
class Solution {
// 定义一个公共方法spiralOrder,该方法接收一个二维整数数组matrix作为参数
// 并返回一个包含按螺旋顺序遍历的元素的List
public List spiralOrder(int[][] matrix) {
// 创建一个ArrayList来存储按螺旋顺序遍历的元素
List result = new ArrayList<>();
// 如果矩阵为空,直接返回空列表
if (matrix == null || matrix.length == 0) {
return result;
}
// 初始化四个边界:上边界、下边界、左边界、右边界
int top = 0, bottom = matrix.length - 1, left = 0, right = matrix[0].length - 1;
// 当上下边界没有交错(即还在矩阵范围内)时,继续遍历
while (top <= bottom && left <= right) {
// 从左到右遍历上边界
for (int i = left; i <= right; i++) {
result.add(matrix[top][i]);
}
// 上边界下移
top++;
// 从上到下遍历右边界
for (int i = top; i <= bottom; i++) {
result.add(matrix[i][right]);
}
// 右边界左移
right--;
// 如果上下边界没有交错,则继续遍历下边界和左边界
if (top <= bottom) {
// 从右到左遍历下边界
for (int i = right; i >= left; i--) {
result.add(matrix[bottom][i]);
}
// 下边界上移
bottom--;
}
if (left <= right) {
// 从下到上遍历左边界
for (int i = bottom; i >= top; i--) {
result.add(matrix[i][left]);
}
// 左边界右移
left++;
}
}
// 返回按螺旋顺序遍历的元素列表
return result;
}
}
## 21. 矩阵置零
给定一个 **`*m* x *n*`**的矩阵,如果一个元素为 **0**,则将其所在行和列的所有元素都设为 **0** 。请使用 **[原地]( )** 算法**。**
### 21.1解法一:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
class Solution {
// 将二维整数矩阵matrix中所有0元素所在的行和列都置为0
public void setZeroes(int[][] matrix) {
// 使用两个列表分别记录0元素所在的行索引和列索引
List listX = new ArrayList<>();
List listY = new ArrayList<>();
// 遍历矩阵的每一个元素
for(int i = 0; i < matrix.length; i++){
for(int j = 0; j < matrix[i].length; j++){
// 如果当前元素为0,则记录其行索引和列索引
if(matrix[i][j] == 0){
listX.add(i);
listY.add(j);
}
}
}
// 使用迭代器遍历列索引列表,并将对应列的所有元素置为0
Iterator<Integer> itY = listY.iterator();
while (itY.hasNext()){
Integer Y = (Integer) itY.next();
for(int i = 0; i < matrix.length; i++){
matrix[i][Y] = 0;
}
}
// 使用迭代器遍历行索引列表,并将对应行的所有元素置为0
Iterator<Integer> itX = listX.iterator();
while (itX.hasNext()){
Integer X = (Integer) itX.next();
for(int j = 0; j < matrix[X].length; j++){
matrix[X][j] = 0;
}
}
}
}
###
## 22.在区间范围内统计奇数数目
题目描述:
给你两个非负整数**`low`** 和 **`high`**。请你返回**`low`**和**`high`**之间(包括二者)奇数的数目。
### 22.1解法一:
### 最后
整理面试题,不是让大家去只刷面试题,而是熟悉目前实际面试中常见的考察方式和知识点,做到心中有数,也可以用来自查及完善知识体系。
**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**
**《前端基础面试题》,《前端校招面试题精编解析大全》,《前端面试题宝典》,《前端面试题:常用算法》**
![](https://img-blog.csdnimg.cn/img_convert/50f4c492aec6709820e902ae921cd96b.webp?x-oss-process=image/format,png)
![前端面试题宝典](https://img-blog.csdnimg.cn/img_convert/5e0bce6995f61e2278a902dc2902a71b.webp?x-oss-process=image/format,png)
![前端校招面试题详解](https://img-blog.csdnimg.cn/img_convert/170ff68a4f1f228ea2e1c6dea9bcdbfb.webp?x-oss-process=image/format,png)
rix[i].length; j++){
// 如果当前元素为0,则记录其行索引和列索引
if(matrix[i][j] == 0){
listX.add(i);
listY.add(j);
}
}
}
// 使用迭代器遍历列索引列表,并将对应列的所有元素置为0
Iterator<Integer> itY = listY.iterator();
while (itY.hasNext()){
Integer Y = (Integer) itY.next();
for(int i = 0; i < matrix.length; i++){
matrix[i][Y] = 0;
}
}
// 使用迭代器遍历行索引列表,并将对应行的所有元素置为0
Iterator<Integer> itX = listX.iterator();
while (itX.hasNext()){
Integer X = (Integer) itX.next();
for(int j = 0; j < matrix[X].length; j++){
matrix[X][j] = 0;
}
}
}
}
22.在区间范围内统计奇数数目
题目描述:
给你两个非负整数**low
** 和 high
。请你返回**low
和high
**之间(包括二者)奇数的数目。
22.1解法一:
最后
整理面试题,不是让大家去只刷面试题,而是熟悉目前实际面试中常见的考察方式和知识点,做到心中有数,也可以用来自查及完善知识体系。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
《前端基础面试题》,《前端校招面试题精编解析大全》,《前端面试题宝典》,《前端面试题:常用算法》
[外链图片转存中…(img-Z40cyNpO-1715789431024)]
[外链图片转存中…(img-ykgYbCPC-1715789431025)]
[外链图片转存中…(img-S7b5ENLc-1715789431025)]