数组——移除元素

文章介绍了如何使用Java、JavaScript和Python实现不同算法来原地修改数组,包括移除特定值、删除重复项和移动零,以及比较含退格的字符串和有序数组的平方。主要方法涉及双指针法和暴力解法,关注点在于空间复杂度和时间效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接

27.移除元素

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

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例1:

	输入:nums = [3,2,2,3], val = 3
	输出:2, nums = [2,2]
	解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例2:

	输入:nums = [0,1,2,2,3,0,4,2], val = 2
	输出:5, nums = [0,1,4,0,3]
	解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100
暴力法

使用2层for循环:第1层循环遍历数组元素,第2层循环更新数组。很明显:其时间复杂度O(n^2),空间复杂度O(1)

Java代码如下:

public class LeetCode27 {
    public static int removeElement(int[] nums, int val) {
        /*1.暴力法:两次for循环*//*
        int size = nums.length;
        for (int i = 0; i < size; i++) {
            if (nums[i] == val) {
                for (int j = i + 1; j < size; j++) {
                    nums[j - 1] = nums[j];
                }
                *//*因为下标i以后的元素(都还未曾处理)都向前移动了1位,所以i也要向前1位*//*
                i--;
                *//*此时数组的大小要-1*//*
                size--;
            }
        }
        return size;*/

        /*2.1快慢指针法:通过1次for循环完成2个for循环的工作*//*
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != val) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;*/

        /*2.2双向指针法:改变元素相对顺序,确保了移动最少元素*/
        int leftIndex = 0;
        int rightIndex = nums.length - 1;
        while (leftIndex <= rightIndex) {
            /*找左边等于val的元素*/
            while (leftIndex <= rightIndex && nums[leftIndex] != val) {
                leftIndex++;
            }
            /*找右边不等于val的元素*/
            while (leftIndex <= rightIndex && nums[rightIndex] == val) {
                rightIndex--;
            }
            /*交换左边和右边的元素*/
            if (leftIndex < rightIndex) {
                nums[leftIndex++] = nums[rightIndex--];
            }
        }
        return leftIndex;
    }

}

JavaScript代码如下:

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
let removeElement = function(nums, val) {
    //方法1和2.1均未改变元素顺序
    /**1.暴力法:使用2个for循环,模拟数组删除过程 */
    // let size = nums.length;
    // for (let i = 0; i < size; i++) {
    //     if (nums[i] == val) {
    //         for (let j = i + 1; j < size; j++) {
    //             nums[j - 1] = nums[j];
    //         }
    //         i--;
    //         size--;
    //     }
    // }
    // return size;

    /**2.1双指针之快慢指针: 使用1次for循环以达到2次for循环的功效 */
    // let leftIndex = 0;

    // for (let rightIndex = 0; rightIndex < nums.length; rightIndex++) {
    //     if (nums[rightIndex] != val) {
    //         nums[leftIndex++] = nums[rightIndex];
    //     }
    // }
    // return leftIndex;

    /**2.2双指针之相向指针:改变了元素相对顺序,但确保了最少的移动次数 */
    let leftIndex = 0,
        rightIndex = nums.length - 1;

    while (leftIndex <= rightIndex) {
        // 左边找到等于val的值,这里对
        while (leftIndex <= rightIndex && nums[leftIndex] != val) {
            leftIndex++;
        }
        //右边找到不等于val的值
        while (leftIndex <= rightIndex && nums[rightIndex] == val) {
            rightIndex--;
        }
        if (leftIndex < rightIndex) {
            nums[leftIndex++] = nums[rightIndex--];
        }
    }
    return leftIndex
};

python代码如下:

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slowIndex = 0
        for fastIndex in range(len(nums)):
            if nums[fastIndex] != val:
                nums[slowIndex] = nums[fastIndex]
                slowIndex += 1
        return slowIndex

26.删除有序数组中的重复项

力扣题目链接
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

示例1:

	输入:nums = [1,1,2]
	输出:2, nums = [1,2,_]
	解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例2:

	输入:nums = [0,0,1,1,1,2,2,3,3,4]
	输出:5, nums = [0,1,2,3,4]
	解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 1 0 4 10^4 104
  • − 1 0 4 -10^4 104 <= nums[i] <= 1 0 4 10^4 104
  • nums已按升序排序

Java代码如下:

public class LeetCode26 {
    public int removeDuplicates(int[] nums) {
        int slowIndex = 1;
        for (int fastIndex = 1; fastIndex < nums.length; fastIndex++) {
            if (nums[fastIndex] != nums[slowIndex - 1]) {
                /**
                 * 优化:当数组本身绝大多数是不重复元素时,减少不必要的复制
                 * 可以考虑最极端的情况:如,[1,2,3,4,5,6],即数组本身即为正确数组
                 * */
                if (fastIndex - slowIndex + 1 > 1) {
                    nums[slowIndex] = nums[fastIndex];
                }
                slowIndex++;
            }
        }
        return slowIndex;

    }
}

JavaScript代码如下:

/**
 * @param {number[]} nums
 * @return {number}
 */
let removeDuplicates = function(nums) {
    let slowIndex = 0;
    for (let fastIndex = 1; fastIndex < nums.length; fastIndex++) {
        if (nums[fastIndex] != nums[slowIndex]) {
            if (fastIndex - slowIndex > 1) {
                nums[slowIndex + 1] = nums[fastIndex];
            }
            slowIndex++;
        }
    }
    return slowIndex + 1;
};

python代码如下:

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        slowIndex, fastIndex = 0, 1
        while fastIndex < len(nums):
            if nums[slowIndex] != nums[fastIndex]:
                if fastIndex - slowIndex > 1:
                    nums[slowIndex + 1] = nums[fastIndex]
                slowIndex += 1
            fastIndex += 1
        return slowIndex + 1

283.移动零

力扣题目链接
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例1:

	输入: nums = [0,1,0,3,12]
	输出: [1,3,12,0,0]

示例2:

	输入: nums = [0]
	输出: [0]

提示:

  • 1 <= nums.length <= 1 0 4 10^4 104
  • − 2 31 -2^{31} 231 <= nums[i] <= 2 31 2^{31} 231 - 1

Java代码如下:

public class LeetCode283 {
    public void moveZeroes(int[] nums) {
        int left = 0, right = 0;
        while (right < nums.length) {
            if (nums[right] != 0) {
                int t = nums[left];
                nums[left] = nums[right];
                nums[right] = t;
                left++;
            }
            right++;
        }
    }
}

JavaScript代码如下:

/**
 * @param {number[]} nums
 * @return {void} Do not return anything, modify nums in-place instead.
 */
let moveZeroes = function(nums) {
    let left = 0, right = 0;
    while (right < nums.length) {
        if (nums[right] != 0) {
            let t = nums[left];
            nums[left] = nums[right];
            nums[right] = t;
            left++;
        }
        right++;
    }
};

Python代码如下:

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        slowIndex = 0
        for fastIndex in range(len(nums)):
            if nums[fastIndex] != 0:
                nums[slowIndex], nums[fastIndex] = nums[fastIndex], nums[slowIndex]
                slowIndex += 1
            fastIndex += 1


844.比较含退格的字符串

力扣题目链接
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

示例1:

	输入:s = "ab#c", t = "ad#c"
	输出:true
	解释:s 和 t 都会变成 "ac"。

示例2:

	输入:s = "ab##", t = "c#d#"
	输出:true
	解释:s 和 t 都会变成 ""。

示例3:

	输入:s = "a#c", t = "b"
	输出:false
	解释:s 会变成 "c",但 t 仍然是 "b"。

提示:

  • 1 <= s.length, t.length <=200
  • s 和 t 只含有小写字母以及字符‘#’

Java代码如下:

public class LeetCode844 {
    public boolean backspaceCompare(String s, String t) {
        char[] chs1 = s.toCharArray();
        char[] chs2 = t.toCharArray();
        return solveBackSpace(chs1).equals(solveBackSpace(chs2));
    }
    public String solveBackSpace(char[] chars) {
        /**
         * 采用快慢指针法:
         * 慢指针指向已处理序列的尾部,即当前符合条件的序列长度
         * 快指针指向未处理序列中符合判断条件的元素
         */
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < chars.length; fastIndex++) {
            if (chars[fastIndex] != '#') {
                chars[slowIndex++] = chars[fastIndex];
            } else if (slowIndex > 0) {
                slowIndex--;
            }
        }
        return new String(chars,0,slowIndex);
    }
}

JavaScript代码如下:

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
let backspaceCompare = function(s, t) {
    return solveBackSpace(s) === solveBackSpace(t);
};
function solveBackSpace(string) {
    let str = "";
    for (const element of string) {
        if (element !== '#') {
            str += element;
        } else if (str.length > 0) {
            str = str.slice(0,str.length - 1);;
        }
    }
    return str;
}

Python代码如下:

class Solution:
    def backspaceCompare(self, s: str, t: str) -> bool:
        return True if self.raw_string(s) == self.raw_string(t) else False

    def raw_string(self, s: str) -> str:
        fastIndex, s1 = 0, ""
        while fastIndex < len(s):
            if s[fastIndex] != '#':
                s1 += s[fastIndex]
            elif len(s1) > 0:
                s1 = s1[:len(s1)-1]
            fastIndex += 1
        return s1

977.有序数组的平方

力扣题目链接
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例1:

	输入:nums = [-4,-1,0,3,10]
	输出:[0,1,9,16,100]
	解释:平方后,数组变为 [16,1,0,9,100]
	排序后,数组变为 [0,1,9,16,100]

示例2:

	输入:nums = [-7,-3,2,3,11]
	输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 1 0 4 10^4 104
  • − 1 0 4 -10^4 104 <= nums[i] <= 1 0 4 10^4 104
  • nums已按非递减顺序排序

Java代码如下:

public class LeetCode977 {
    public int[] sortedSquares(int[] nums) {
        /**
         * 1.暴力法:先平方在排序
         * 时间复杂度为:O(n + nlogn)
         * for (int i = 0; i < nums.length; i++) {
         *     nums[i] *= nums[i];
         * }
         * java.util.Arrays.sort(nums);
         * return nums;
         * */

        /**
         * 2.双指针法:相向指针,确定平方后的最大元素,直接放于新数组尾部
         */
        int rightIndex = nums.length  - 1;
        int leftIndex = 0;
        int[] result = new int[nums.length];
        int newIndex = result.length - 1;
        while (leftIndex <= rightIndex) {
            if (nums[leftIndex] * nums[leftIndex] > nums[rightIndex] * nums[rightIndex]) {
                result[newIndex--] = nums[leftIndex] * nums[leftIndex];
                leftIndex++;
            } else {
                result[newIndex--] = nums[rightIndex] * nums[rightIndex];
                rightIndex--;
            }
        }
        return result;
    }

}

JavaScript代码如下:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
let sortedSquares = function(nums) {
    /*1.暴力解法
    for (let i = 0; i < nums.length; i++) {
        nums[i] = Math.pow(nums[i], 2);
    }
    nums.sort((a, b) => a-b);
    return nums;
    */
    /*2.双向指针法:确定最大的平方数,并放入新数组中*/
    let rightIndex = nums.length - 1;
    let result = new Array(nums.length).fill(0);
    let newIndex = result.length - 1;
    for (let leftIndex = 0; leftIndex < rightIndex;) {
        if (Math.pow(nums[leftIndex], 2) > Math.pow(nums[rightIndex], 2)) {
            result[newIndex--] = Math.pow(nums[leftIndex], 2);
            leftIndex++;
        } else {
            result[newIndex--] = Math.pow(nums[rightIndex], 2);
            rightIndex--;
        }
    }
    return result;
};
console.log(sortedSquares([-4,-1,0,3,10]));

Python代码如下:

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        count, leftIndex, rightIndex, ans = len(nums)-1 , 0, len(nums)-1, [0 for x in range(len(nums))]
        while leftIndex <= rightIndex:
            if nums[leftIndex] ** 2 >= nums[rightIndex] ** 2:
                ans[count] = nums[leftIndex] ** 2
                leftIndex += 1
            else:
                ans[count] = nums[rightIndex] ** 2
                rightIndex -= 1
            count -= 1
        return ans

总结:

采用快慢指针法:

  1. 慢指针指向已经处理好序列的尾部,即其长度。
  2. 快指针指向未处理序列的符合条件的元素。

采用双向指针法:

  1. 改变了元素相对顺序(可根据题意看是否限制)。
  2. 确保了最少的移动次数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值