Leetcode移除元素+有序数组的平方题组27/26/283/844/977

前言

移除元素

一、27题(移除元素)

题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

解释:public int removeElement(int[] nums, int val)
虽然用例的输出是数组,但是只用返回剩余元素的个数。且要求移除元素后的新nums是连续的。

算法思路(快慢指针)

动画题解
快指针(f)和慢指针(s)均在数组上从左到右移动。但慢指针在遇到val值元素时会停下,等待快指针移动到下一个非val值处,并把值传给它。
流程图

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow=0;
        for(int fast=0;fast<nums.length;){
            if(nums[fast]==val){
                fast++;
            }else{
                nums[slow] = nums[fast];
                slow++;
                fast++;
            }
        }
        return slow;
    }
}

算法思路:
1、快指针指向的元素不等于val时,f和s同时向右移动,并完成赋值。f指向的元素等于val时,只有f向右移动。(图解分析很容易明白)
2、最终慢指针处就是新数组元素个数。

二、26题(删除排序数组中的重复项)

题目描述:给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。

class Solution {
    public int removeDuplicates(int[] nums) {
        int slow = 0;
        for(int fast = 1;fast<nums.length;){
            if(nums[fast] == nums[slow]){
                fast++;
            }else{
                nums[++slow] = nums[fast++];
            }
        }
        return slow+1;
    }
}

算法分析:

  • 仍然是快慢指针模板
  • 思路:fast指针遍历数组,当指向的数和slow目前指的数不一样时,执行nums[++slow]=nums[fast++],即把不重复元素存下来。当是重复元素时,fast继续往前走。

三、283题(移动零)

题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
1ms的题解(mine)

class Solution {
    public void moveZeroes(int[] nums) {
        //本质是移除元素0
        int slow = 0;
        for(int fast = 0;fast<nums.length;){
            if(nums[fast]!=0){
                nums[slow++] = nums[fast++];
            }
            else{
                fast++;
            }
        }
        // 此时,非0元素个数为slow个
        //nums数组中前slow个元素为非0元素
        //只要把slow开始到末尾的赋值为0即可
        for(int i = slow;i<nums.length;i++){
            nums[i] = 0;
        }
    }
}

0ms的题解(别人的)

public class Solution {
    public void moveZeroes(int[] nums) {
        int idx = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                nums[idx]=nums[i];
                idx++;
            }
        }
        while(idx<nums.length){
            nums[idx++]=0;
        }
    }
}

看起来也差不多,区别不大/(ㄒoㄒ)/
算法分析:

  • 题目的本质是移除0元素,所以和第一道题一样

四、844题(比较含退格的字符串)

题目描述:给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:s = “ab#c”, t = “ad#c”
输出:true
解释:s 和 t 都会变成 “ac”。

1、初次做的结果:对JAVA的String类不熟悉。String类无法直接通过下标获取对应元素,要通过charAt()/length()等方法。以及无法直接修改,String是一个常量,使用方法来修改的话时间复杂度为O(n),故应该转化成char数组来处理。
2、以及出现的一个算法上的错误:返回新串的个数是错误的,应该是返回新串。旧串的前nums.length个元素的比较没有任何意义。

class Solution {
    public boolean backspaceCompare(String s, String t) {
        int sl = changeString(s);
        int tl = changeString(t);
        if(sl==tl){
            for(int i = 0;i<sl;i++){
                if(s.charAt(i)!=t.charAt(i)){
                    return false;
                }
            }
            return true;
        }
        else{
            return false;
        }
    }
    public int changeString(String nums) {
        int slow = 0;
        for(int fast = 0;fast<nums.length();){
            if(nums.charAt(fast)!='#'){
                nums.charAt(slow) = nums.charAt(fast);
                slow++;
                fast++;
            }
            else{
                fast++;
                slow--;
            }
        }
        return slow-1;
    }
}

修改后的算法:

class Solution {
    public boolean backspaceCompare(String s, String t) {
        s = changeString(s);
        t = changeString(t);
        if(s.length()==t.length()){
            for(int i = 0;i<s.length();i++){
                if(s.charAt(i)!=t.charAt(i)){
                    return false;
                }
            }
            return true;
        }
        else{
            return false;
        }
    }
    public String changeString(String s) {
        char[] nums = s.toCharArray();//转换成char数组
        int slow = 0;
        for(int fast = 0;fast<nums.length;){
            if(nums[fast]!='#'){
                nums[slow++] = nums[fast++];
            }
            else{
                fast++;
                if(slow!=0){//易错点:a##c
                    slow--;
                }
            }
        }
        //新数组元素有slow-1个
        return new String(nums).substring(0, slow);
    }
}

算法分析:

  • 仍然使用快慢指针思路,快指针遍历数串,慢指针指向新串,便于更新。核心思路:快指针指向元素需要判断是否符合题目要求,以此更新slow指向的元素。slow相当于是新串指针。
  • 实时分析"ac#b"输出"ab":
    ①初始化,fast指向a,slow指向a。
    ②判断"fast处元素不等于#“,故nums[slow]=nums[fast],并均向右移动。
    即代码中的nums[slow++]=nums[fast++]
    ③ 判断"fast处元素等于#”,故要退格,slow–,fast继续向右移动。
    ④判断"fast处元素不等于#",故nums[slow]=nums[fast]。
    ⑤最终输出ab,元素个数为slow-1。
  • 注意点(易错点):已在代码中标明
    如"a##c",会遇到两次退格,即slow–,会变成负数,但数组下标不可以是负数。所以要判断遇到退格时当前slow是否为0。

五、977题(有序数组的平方)

题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
简洁版(别人的)

class Solution {
    public int[] sortedSquares(int[] nums) {
        int right = nums.length - 1;
        int left = 0;
        int[] result = new int[nums.length];
        int index = result.length - 1;
        while (left <= right) {
            if (nums[left] * nums[left] > nums[right] * nums[right]) {
                // 正数的相对位置是不变的, 需要调整的是负数平方后的相对位置
                result[index--] = nums[left] * nums[left];
                ++left;
            } else {
                result[index--] = nums[right] * nums[right];
                --right;
            }
        }
        return result;

    }
}

算法分析:

-双指针,两头比较(平方较大数分布在两端)

我的解法(mine)

class Solution {
    public static int[] sortedSquares(int[] nums) {
	        int[] nums2 = new int[nums.length];
	        //全是大于等于零的数(直接输出平方)
            if(nums[0]>=0){
                for(int i=0;i<nums.length;i++){
                    nums2[i]=nums[i]*nums[i];
                }
                return nums2;
            }
            if(nums[nums.length-1]<0){
                for(int i=0;i<nums.length;i++){
                    nums2[i]=nums[nums.length-1-i]*nums[nums.length-1-i];
                }
                return nums2;
            }
	        //处理有负数有正数的情况
	        int ans = 0; 
	        while(nums[ans]<0){
	            ans++;
	        }
	        //找到第一个非负元素下标ans
	        int p1 = ans-1;//负数指针
	        int p2 = ans;//非负数指针
	        int k = 0 ;
	        while(p1>=0&&p2<nums.length){
	            nums2[k++] = (-nums[p1])<nums[p2]?nums[p1--]:nums[p2++];
	        }
	        //处理剩余元素
	        //因为每次只有一个指针移动,所以必然要么是p1<0退出,要么是p2=nums.length退出
	        //不可能同时满足
	        if(p1<0){
	            //表示p2处还有剩余元素未处理
	            while(p2<nums.length){
	                nums2[k++] = nums[p2++];
	            }
	        }
	        if(p2==nums.length){
	            //表示p2处还有剩余元素未处理
	            while(p1>=0){
	                nums2[k++] = nums[p1--];
	            }
	        }
	        //此时num2已经按照绝对值从小到大排序
            for(int i=0;i<nums2.length;i++){
                nums2[i] = nums2[i]*nums2[i];
            }
            return nums2;
	    }
}

算法分析:

  • 数组全为负或全为非负时直接输出答案,重点在于处理同时存在负数和非负数的情况。
  • 对于数组"-4,-1,0,3,10":首先找到第一个非负数下标,为ans=2。(则该数组分为两部分,[0,ans-1]和[ans,nums.length-1])设置p1指针向左移动遍历负数组,p2指针向右移动遍历整数组。边比较边将数字填入新数组之中。具体思路见代码,标注的很清晰了😀

以下是一些补充代码:

用库函数竟然也行(○´・д・)ノ
用时5ms

class Solution {
    public int[] sortedSquares(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
               nums[i]=nums[i]*nums[i];
        }
        Arrays.sort(nums);
        return nums;
    }
}

和我的解法一样(看来咱才是正统方法(‾◡◝))

class Solution {
    public int[] sortedSquares(int[] nums) {
        //找到正负元素的分界线,然后左右判断元素
        int line=-1;
        int[] result = new int[nums.length];
        for(int i=0;i<nums.length;i++){
            if(nums[i]<0){
                line++;
            }else{
                break;
            }
        }
        System.out.print(line);
        // line是个分界线 line所在的位置是0或者正数 line-1为负数 
        
        // 说明有正有负
        int l = line;
        int r = line+1;
        int j=0;
        while(l>=0&&r<=nums.length-1){
            if(-1*nums[l]>nums[r]){
                result[j] = nums[r]*nums[r];
                r++;
            }else{
                result[j] = nums[l]*nums[l];
                l--;
            }
            j++;
        }
        while(l>=0){
            result[j++] = nums[l]*nums[l];
            l--;
        }
        while(r<=nums.length-1){
            result[j++] = nums[r]*nums[r];
            r++;
        }
        return result;
    }
}

备注

前四题均使用快慢指针。最后一题是双指针。

  • 43
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值