27.移除元素
快慢指针解法
快指针遍历整个数组:快指针遍历数组的每个元素,判断该元素是否等于给定val
如果快指针遍历到的元素不等于val,则将其赋值给慢指针位置
慢指针用于记录不等于val的元素的位置
当快指针遍历到不等于val的元素时,慢指针才需要+1,以确保不等于val的元素保留在数组前面
慢指针的值即为数组中不包含val的元素数量
public int removeElment(int[] nums,int val){
int slow = 0;
for(int fast = 0;fast<nums.length;fast++){
if(nums[fast] != val){
nums[slow] = nums[fast];
slow++;
}
}
return slow
}
暴力双层循环解法
外层循环:遍历整个数组,找到需要移除的元素val
内层循环:一旦找到val,就把它后面的元素前移,覆盖当前的val
public int removeElment(int[] nums,int val){
int n = nums.lenght;
for(int i = 0;i < n;i++){
if(nums[i] == val){
for(int j = i;j<n-1;j++){//内层循环,后面的元素前移
nums[j] = nums[j + 1];
}
n--;//数组长度减一
i--;//继续检查当前位置,防止新移过来的元素被跳过
}
}
return n;
}
数组 = 3,2,2,4,5
value = 2
外层循环,首先遍历到i=1,发现数组 i = 2,这就是我们要移除的元素
进入内层循环,将后面的元素逐个向前移
数组i = 2后面的元素的值逐个向前移动
num 1会被 num 2的值2替代 3,2,2,4,5
num 2会被 num 3的值2替代 3,2,4,4,5
num 3会被 num 4的值2替代 3,2,4,5,5
3,4,5,5,5
26.删除有序数组中的重复项
在重复项的位置覆盖元素
去重,默认第一个元素一定是唯一的thats why slow=1
题目要求
- 原地去重:不能使用额外的数组,必须在nums本身修改
- 保持相对顺序
- 返回唯一元素的个数k,并让nums的前k个元素存放去重后的值
快慢指针的作用
-
快指针fast
- 负责遍历数组,找到不重复的元素
- 遇到不相等的值,就告诉慢指针存储该值
-
慢指针slow
- 负责存放唯一的元素,保证去重后元素紧凑存储在nums的前k个位置
- 每当fast发现新元素,就写入nums【slow】,并移动slow指向下一个可存放的位置
class Solution {
public int removeDuplicates(int[] nums) {
int slow = 1; // 慢指针:指向存放新元素的位置
for (int fast = 0; fast < nums.length - 1; fast++) { // 快指针遍历整个数组
if (nums[fast] != nums[fast + 1]) { // 找到新的不同的数
nums[slow] = nums[fast + 1]; // 放到 slow 指针的位置
slow++; // 慢指针移动
}
}
return slow; // slow 代表唯一元素的个数 k
}
}
2460.对数组执行操作
双指针解决
class Solution {
public int[] applyOperations(int[] nums) {
// 1. 先合并相邻相同的数
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] == nums[i + 1]) {
nums[i] *= 2; // 合并
nums[i + 1] = 0; // 置 0
}
}
// 2. 移动零(使用嵌套循环)
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) { // 找到 0
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] != 0) { // 找到后面的非零元素
// 交换 nums[i] 和 nums[j]
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
break; // 交换一次就跳出内循环
}
}
}
}
return nums;
}
}
118.杨辉三角
1295. 统计位数为偶数的数字
Java如何将数字转换为字符串
法一:使用String.valueOf()
int num = 123;
String str = String.valueOf(num);
法二:使用Integer.toString()
专门用于将整数转换为字符串
int num = 456;
String str = Integer.toString(num);
法三:使用字符串拼接
通过将数字与空字符串""
拼接,Java会自动将数字转换为字符串
int num = 789
String str = num + "";
for(int num:nums)
是Java中的增强for循环(for-each循环)一种简化遍历数组或集合的语法
适合只需要访问数组或集合中的每个元素,而不需要操作索引时
增强for循环的语法
for(元素类型 变量名 : 数组或集合名){
//使用变量名操作当前元素
}
- 元素类型:数组或集合中元素的类型(
int
,String
) - 变量名:一个临时变量名,用于存储当前遍历到的元素
- 数组或集合:需要遍历的数组或集合
区别:
特性 | 增强 for 循环 | 普通 for 循环 |
---|---|---|
语法简洁性 | 更简洁,适合遍历数组或集合中的元素 | 需要手动管理索引,语法稍复杂 |
索引访问 | 无法直接访问索引 | 可以通过索引访问元素 |
修改数组或集合 | 只能读取元素,不能修改数组或集合 | 可以通过索引修改数组或集合中的元素 |
适用场景 | 适合只读遍历 | 适合需要索引操作的场景 |
使用场景
- 遍历数组:
int[] nums = {1,2,3,4,5};
for(int num:nums){
sysyout(num);
}
- 遍历集合
List<String> list = Arrays.asList("A","B","C");
for(String str : list){
sysout(str);
}
- 遍历多维数组
int[][] matrix = {{1,2},{3,4}};
for(int[] row : matrix){
for(int num : nums){
sysout(num);
}
}
题目解决
class Solution(int[] nums){
int count = 0
for(int num : nums){
String str = String.valueOf(num);
if(str.length() % 2 == 0){
count++;
}
}
return count;
}
2094. 找出 3 位偶数
349. 两个数组的交集
List<Integer> resultList = new ArrayList<>();
是 Java 中创建列表(List)的一种方式。
1. List<Integer>
是什么?
List
是 Java 中的一个接口,表示一个有序的集合(也称为序列)。它可以存储多个元素,并且允许重复元素。List
接口的常见实现类有 ArrayList
、LinkedList
等。
Integer
:这是List
中存储的元素类型。Integer
是 Java 中的包装类,用于表示基本数据类型int
的对象形式。List<Integer>
:表示一个可以存储Integer
类型元素的列表。
2. ArrayList<>()
是什么?
ArrayList
是 List
接口的一个实现类。它是一个基于动态数组的数据结构,支持快速随机访问和动态扩容。
new ArrayList<>()
:这是创建ArrayList
对象的语法。<>
是 Java 中的泛型语法,表示这个列表可以存储任意类型的元素。在这里,<>
中的类型被推断为Integer
,因为前面已经声明了List<Integer>
。
3. List<Integer> resultList = new ArrayList<>();
的作用
这行代码的作用是创建一个名为 resultList
的列表,用于存储 Integer
类型的元素。这个列表可以用来动态地添加、删除或访问元素。
- 动态大小:
ArrayList
的大小是动态的,可以根据需要自动扩容。 - 有序性:列表中的元素是有序的,按照添加的顺序存储。
- 允许重复:列表中可以存储重复的元素。
4. 为什么在这里使用 List<Integer>
?
在你的代码中,resultList
用于存储两个数组的交集结果。使用 List<Integer>
的原因包括:
- 动态添加元素:在找到交集元素时,可以方便地使用
resultList.add(num)
将元素添加到列表中。 - 去重:可以通过
resultList.contains(num)
检查元素是否已经存在于列表中,从而避免重复添加。 - 最终转换为数组:在返回结果时,可以将
List<Integer>
转换为int[]
,方便返回。
5. List<Integer>
的常用方法
以下是一些 List<Integer>
的常用方法:
add(Integer element)
:向列表中添加一个元素。contains(Object element)
:检查列表中是否包含某个元素。get(int index)
:获取列表中指定位置的元素。size()
:返回列表中元素的数量。remove(Object element)
:从列表中移除某个元素。
6. 示例代码
以下是一个简单的示例,展示如何使用 List<Integer>
:
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String[] args) {
// 创建一个 List<Integer>
List<Integer> numbers = new ArrayList<>();
// 添加元素
numbers.add(1);
numbers.add(2);
numbers.add(3);
// 检查是否包含某个元素
System.out.println("Contains 2? " + numbers.contains(2)); // 输出: true
// 获取元素
System.out.println("Element at index 1: " + numbers.get(1)); // 输出: 2
// 遍历列表
for (int num : numbers) {
System.out.println(num);
}
}
}
1351. 统计有序矩阵中的负数
Java 数组系统教程
一、一维数组
1. 声明与初始化
Java 数组是固定长度的容器,存储相同类型的元素。
// 声明方式(两种等价)
int[] arr1; // 推荐
int arr2[]; // 不推荐,易与变量名混淆
// 初始化方式
int[] arr3 = {1, 2, 3}; // 直接赋值(静态初始化)
int[] arr4 = new int[3]; // 动态初始化(默认值为0)
int[] arr5 = new int[]{1, 2, 3}; // 动态初始化(需显式赋值)
2. 访问与遍历
通过索引访问数组元素(索引从 0
开始):
System.out.println(arr3[0]); // 输出 1
arr4[1] = 5; // 修改元素
// 遍历方式(三种)
// 1. for 循环
for (int i = 0; i < arr3.length; i++) {
System.out.println(arr3[i]);
}
// 2. for-each 循环(只读遍历)
for (int num : arr3) {
System.out.println(num);
}
// 3. 使用 Arrays.toString()
System.out.println(Arrays.toString(arr3)); // 输出 [1, 2, 3]
二、二维数组
1. 声明与初始化
二维数组是“数组的数组”,每行可以有不同的长度(但题目中的矩阵通常等长)。
// 声明方式
int[][] matrix1; // 推荐
int matrix2[][]; // 不推荐
// 初始化方式
int[][] matrix3 = {{1, 2}, {3, 4, 5}}; // 静态初始化(允许不等长)
int[][] matrix4 = new int[2][3]; // 动态初始化(等长矩阵)
int[][] matrix5 = new int[][]{{1}, {2, 3}};
2. 访问与遍历
// 访问元素
System.out.println(matrix3[0][1]); // 输出 2
// 遍历二维数组(正确方式)
for (int i = 0; i < matrix3.length; i++) { // 遍历行
for (int j = 0; j < matrix3[i].length; j++) { // 遍历当前行的列
System.out.print(matrix3[i][j] + " ");
}
System.out.println();
}
关键点:
matrix.length
表示行数。matrix[i].length
表示第i
行的列数。
三、常见错误与解决方法
1. 数组越界(ArrayIndexOutOfBoundsException)
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // ❌ 索引最大为2
解决方法: 确保索引 < arr.length
。
2. 空指针异常(NullPointerException)
int[][] matrix = new int[2][];
System.out.println(matrix[0][0]); // ❌ matrix[0] 未初始化,为null
解决方法: 初始化每一行:
for (int i = 0; i < matrix.length; i++) {
matrix[i] = new int[3]; // 每行初始化3列
}
3. 遍历条件错误
用户原代码错误:
for (int j = 0; j < grid.length; j++) // ❌ 错误使用行数作为列循环条件
修正:
for (int j = 0; j < grid[i].length; j++) // ✅ 使用当前行的列数
四、性能优化(针对题目)
题目中的矩阵按行和列非递增排序,可以利用有序性优化遍历:
public int countNegatives(int[][] grid) {
int count = 0;
int col = grid[0].length;
for (int[] row : grid) {
// 从右往左找到第一个非负数,后面的元素全为负数
int j = col - 1;
while (j >= 0 && row[j] < 0) {
j--;
}
count += (col - j - 1);
}
return count;
}
优化点:
- 时间复杂度从 O(m*n) 降为 O(m+n),适用于大规模数据。
- 利用矩阵有序性,减少不必要的遍历。
五、总结
知识点 | 关键内容 |
---|---|
一维数组初始化 | 静态初始化 {} ,动态初始化 new int[size] |
二维数组结构 | 行数:array.length ,列数:array[i].length |
遍历方式 | 嵌套循环,外层遍历行,内层遍历当前行的列 |
常见错误 | 越界、空指针、循环条件错误 |
优化技巧 | 利用有序性减少遍历次数 |
88. 合并两个有序数组
(阿里云一面)
合并两个有序数组
题目描述
给定两个按非递减顺序排列的整数数组 nums1
和 nums2
,其中 nums1
的长度为 m + n
,前 m
个元素为有效元素,后 n
个元素初始化为0。要求将 nums2
合并到 nums1
中,使得合并后的数组仍保持非递减顺序。最终结果应存储在 nums1
中。
示例
-
示例1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
-
示例2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
-
示例3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解题思路
由于需要直接在 nums1
中合并,且 nums1
的末尾有足够的空间,可以采用从后向前合并的方法。这种方法避免了从前向后合并时可能出现的元素覆盖问题。具体步骤如下:
-
初始化指针
p1
:指向nums1
有效部分的末尾(索引m-1
)。p2
:指向nums2
的末尾(索引n-1
)。p
:指向合并后数组的末尾(索引m+n-1
)。
-
从后向前合并
比较nums1[p1]
和nums2[p2]
,将较大的元素放入nums1[p]
的位置,并将对应指针向前移动。重复此过程直到其中一个数组遍历完成。 -
处理剩余元素
如果nums2
中仍有元素未合并,说明这些元素是剩余最小的,直接复制到nums1
的前部。
代码实现
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1;
int p2 = n - 1;
int p = m + n - 1;
while (p1 >= 0 && p2 >= 0) {
if (nums1[p1] > nums2[p2]) {
nums1[p] = nums1[p1];
p1--;
} else {
nums1[p] = nums2[p2];
p2--;
}
p--;
}
// 处理nums2剩余元素
while (p2 >= 0) {
nums1[p] = nums2[p2];
p2--;
p--;
}
}
}
复杂度分析
- 时间复杂度:O(m + n),每个元素最多被访问一次。
- 空间复杂度:O(1),无需额外空间。
关键点说明
- 逆向遍历:避免了正向遍历时的覆盖问题,确保已处理的元素不会被覆盖。
- 剩余元素处理:只需处理
nums2
的剩余元素,因为nums1
的剩余元素已处于正确位置。 - 边界条件:当
m = 0
或n = 0
时,代码依然能正确处理。
3285. 找到稳定山的下标
1.数组长度获取
在Java中,数组是一个固定大小的对象,其长度通过属性length而不是方法来访问
int[] length = {1,2,3};
int len = height.length;
2.循环边界条件
当我需要遍历一个数组时,通常会使用for循环,但需要注意边界条件避免数组越界异常
对于一个长度为n的数组,有效的索引是从0
到n-1
for(int i = 1;i < height.length;i++){
//这里i从1开始,遍历到height.length - 1
}
3.列表的初始化与使用
import java.util.ArrayList;
import java.util.List;
List<Integer> list = new ArrayList<>();
list.add(1);
4.返回类型
函数声明返回类型为List<Integer>
,意味着我需要返回一个包含整数的列表,在Java中,我可以通过创建一个列表对象,向其中添加所需元素,最后返回列表对象实现
public List<Integer> stableMountains(int[] height,int threshold){
List<Integer> list = new ArrayList<>();
for(int i = 1;i<height.length;i++){
if(height[i-1] == threshold){
list.add(i);
}
}
return list;
}
最近做的一些leetcode题目记录🫡