数组刷题笔记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;
    }
}
posted @ 2022-04-28 21:48  chachan53  阅读(52)  评论(0编辑  收藏  举报