数组刷题笔记2:移除元素
移除元素
27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
思路
数组中的元素只能覆盖,不能删除
方法1:暴力遍历
用双重for循环遍历,寻找到val时将后续元素向前移一位
方法2:双指针(快慢指针法)
设置两个指针,一个快指针和一个慢指针。
令慢指针在元素非val的时候向前移动,为val的时候停顿,并持续将快指针的指向的值赋予慢指针,覆盖val的值
最后慢指针的值就是数组的长度。
- 复杂度:
- 时间:O(n)
- 空间:O(1)
class Solution {
public int removeElement(int[] nums, int val) {
//单向双指针法
//一个指针指要删除的数,一个指下一个数
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
//为val值时slowIndex停滞,下一次循环时快指针指向的内容替换慢指针指向的val
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
}
方法3:相向双指针法
设置左右两个指针:
- 左指针从左向右遍历,找到val值停下
- 右指针从右向左遍历,找到非val值停下
- 将右指针指向的值覆盖val
- 循环,直至循环结束,返回左指针的值或右指针+1
在遍历时,右指针遇到的val值必然超出结果所得到的数组的界限,是要被抛弃的val,不需要进行覆盖操作。到两根指针相遇时,左指针指向数组的后一位,而右指针必然在左指针前一位(循环条件为left<=right),返回值为剩余数组的长度。
class Solution {
public int removeElement(int[] nums, int val) {
//双向双指针法
//一个指针指要删除的数,一个指不要删除的数
int n = nums.length;
int leftIndex = 0, rightIndex = n - 1;
while (leftIndex <= rightIndex) {
//左指针从左开始找val
while (leftIndex <= rightIndex && nums[leftIndex] != val) {
++leftIndex;
}
//右指针从右开始找非val
while (leftIndex <= rightIndex && nums[rightIndex] == val) {
--rightIndex;
}
//把右边的非val数替换到前面的val中,直接忽视末尾的val
if (leftIndex < rightIndex) {
//替换之后继续遍历
nums[leftIndex++] = nums[rightIndex--];
}
}
//leftIndex指向末尾的下一个元素,即数组长度;或rightIndex + 1(循环结束条件)
return rightIndex + 1;
}
}
26. 删除有序数组中的重复项
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。
将最终结果插入 nums 的前 k 个位置后返回 k 。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
思路
通过快慢指针法,通过对比重复元素的使慢指针停滞,向前覆盖。
与27的区别在于重复元素的判定,此处将起始数据设定为1,避免在遍历时漏去最后一个元素,并保存第一个元素。将相邻的元素进行对比,重复时慢指针停滞。
class Solution {
public int removeDuplicates(int[] nums) {
//快慢指针法,遍历函数,val值变化,如果等于val值慢指针停滞
//第一个数保留,第一个数必然不会重复。
int slowIndex = 1;
//和前一个数做对比,避免数组越界和漏掉最后一个数
for(int fastIndex = 1; fastIndex < nums.length; fastIndex++) {
if(nums[fastIndex - 1] != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
}
283. 移动零
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作
思路
关注的重点:0移到末尾,保持数组的相对顺序
- 找出数组中的零,用0后面的数覆盖0
- 将调整后的数组的末尾覆盖为0,达到移动0到末尾的效果
class Solution {
public void moveZeroes(int[] nums) {
//快慢指针,覆盖0,将最后几个数组赋值为0(相向指针会改变相对顺序)
int slowIndex = 0;
for (int fastIndex = 0;fastIndex < nums.length;fastIndex++) {
if (nums[fastIndex] != 0) {
nums[slowIndex++] = nums[fastIndex];
}
}
for(;slowIndex < nums.length;slowIndex++) {
nums[slowIndex] = 0;
}
}
}
844. 比较含退格的字符串
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
方法1:快慢指针法
设置快指针和慢指针,判断是否为退格“#”,当为#时,慢指针往回退一格,然后将快指针指向的值赋予慢指针。
当第一个字符为“#”,删除退格字符
- 复杂度:
- 时间复杂度O(N+M)
- 空间复杂度O(N+M),还原的开销
class Solution {
public boolean backspaceCompare(String s, String t) {
//使用快慢指针,一个按位前进,一个在遇到退格的时候指向前一个元素
//用equals比较字符串
return backDelete(s).equals(backDelete(t));
}
//对退格进行处理,重构字符串
String backDelete(String str) {
//将字符串转换为字符数组
char c[] = str.toCharArray();
int slowIndex = 0;
for (int fastIndex = 0;fastIndex < c.length;fastIndex++) {
//如果不为#,赋值;如果为#,指向上一个元素
//如果第一个为#,慢指针不变,快指针指向下一个元素
if (c[fastIndex] != '#'){
c[slowIndex++] = c[fastIndex];
} else if (slowIndex > 0){
slowIndex--;
}
}
return String.valueOf(c, 0, slowIndex);//0是起始位置,slowIndex是结束位置
}
}
方法2:从后往前法
从后往前,对每个字符进行对比
- 复杂度:
- 时间复杂度O(N+M)
- 空间复杂度O(1)
class Solution {
public boolean backspaceCompare(String s, String t) {
//逆序对比
int i = s.length() - 1, j = t.length() - 1;
int skipS = 0, skipT = 0;
while (i >= 0 || j >= 0){
//如果都不为退格,进行对比,如果字符不相同,字符串必然不同
//1. 退格处理
//如果为退格符,指针前移,退格符计数增加,在下一循环中指针再次前移,以达到退格的目的
//同时保证数组不越界
while (i >= 0) {
if (s.charAt(i) == '#') {
i--;
skipS++;
} else if (skipS > 0) {
i--;
skipS--;
} else {
break;
}
}
while (j >= 0) {
if (t.charAt(j) == '#') {
j--;
skipT++;
} else if (skipT > 0) {
j--;
skipT--;
} else {
break;
}
}
//2. 对比字符是否相同
if (i >= 0 && j >= 0) {
if (s.charAt(i) != t.charAt(j)) {
return false;
}
} else if (i >= 0 || j >= 0) {//长度不同,字符串必然不相等
return false;
}
//进行下一个字符的对比
i--;
j--;
}
return true;
}
}
977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
- 思路:
- 左右两个指针,对比平方数的大小,将更大的数的平方填入结果数组
- 继续对比,循环填入结果
- 复杂度:
- 时间复杂度O(N),遍历一次数组
- 空间复杂度O(N),结果数组
class Solution {
public int[] sortedSquares(int[] nums) {
//双指针,左右平方对比,按顺序排列进数组
int n = nums.length;
int left = 0, right = n - 1, i = n - 1;
int[] result = new int[n];//创建放置排序结果的数组
while (left <= right) {
if (nums[left] * nums[left] < nums[right] * nums[right]) {
result[i] = nums[right] * nums[right];
right--;//右指针向左
i--;
} else {
result[i] = nums[left] * nums[left];
left++;//左指针向右
i--;
}
}
return result;
}
}