LeetCode-数组移出元素与删除重复项,返回操作后的数组长度

这是一个关于LeetCode问题的博客,主要涉及如何在数组中移除特定值的元素和删除重复项。对于给定的示例输入如[3,2,2,3] (val=3) 和 [1,1,2],函数应返回新数组的长度,并确保数组前部包含未重复的元素。在删除操作后,数组长度分别为2和2,且超出新长度的元素不考虑。在处理数组时,需要注意遍历的起点,以避免特殊情况如全0数组导致的错误。" 107677976,9952952,理解Java递归:计算5的阶乘,"['Java', '算法', '递归', '编程基础']

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

移出元素
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
记得返回的是数组的长度

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if(nums.empty())
            return NULL;
        int num=0;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]!=val)
            nums[num++]=nums[i];    
        }
        return num;
    }
};

删除重复项
给定数组 nums = [1,1,2],

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

你不需要考虑数组中超出新长度后面的元素。
这里需要注意遍历时i不能从0起,否则测试案例[0,0,0…]会没有输出。
记得返回的是数组的长度

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.empty())
           return NULL;
           int num=1;
           for(int i=1;i<nums.size();i++)
           {
               if(nums[i]==nums[i-1])
                   continue;
                   nums[num++]=nums[i];           
           }
           return num;
    }
};
<think>嗯,用户的问题是要找出一个数组中不含重复数字的最长子数组长度,然后编写程序。我得先仔细理解这个问题。首先,题目中的“子数组”应该是指连续的子数组吧?因为如果不连续的话,那就是子序列的问题了,而题目里明确说是子数组,所以应该是连续的。那目标就是找到最长的连续子数组,里面的元素都不重复。 接下来,我需要想有什么方法可以解决这个问题。常见的算法问题中,涉及到子数组或者子字符串的最长不重复问题,通常会使用滑动窗口的方法。比如,LeetCode上的第三题“无重复字符的最长子串”就是这个类型的问题。那这个问题应该可以用同样的思路来解决。 那滑动窗口的具体实现是怎样的呢?通常需要两个指针,左指针右指针,分别表示窗口的左右边界。右指针不断右移,如果遇到重复的元素,就移动左指针到重复元素的下一个位置,同时更新当前的最大长度。在这个过程中,需要用一种数据结构来记录窗口内的元素是否重复,比如哈希集合或者哈希表,这样可以在O(1)的时间复杂度内判断元素是否存在。 那具体到数组的情况,比如数组中的元素是数字,可能有负数吗?或者有没有其他限制?题目没有说明,所以应该假设元素可以是任意整数。这时候,使用哈希集合来存储当前窗口中的元素,当右指针指向的元素已经存在于集合中时,就需要移动左指针,直到这个元素移出集合。然后继续移动右指针,同时更新最大长度。 但是,这里有一个需要注意的问题,当发现重复元素时,左指针不能直接跳到之前重复元素的下一个位置。比如,当窗口是[left, right],当right+1位置的元素已经在集合中存在时,需要将左指针移动到max(left, 重复元素的位置+1),否则可能会出现左指针回退的情况。例如,假设之前的窗口中有一个重复元素的位置是i,而当前的左指针已经移动到i+1之后的位置,这时候如果遇到另一个重复元素的位置j,那么左指针应该取较大的那个,即max(left, j+1)。这样才能保证窗口内没有重复元素。 这个时候,可能需要使用哈希表来记录每个元素最后一次出现的位置,这样当遇到重复元素时,可以直接找到它之前的位置,然后调整左指针的位置。例如,当遍历到元素nums[right]时,如果它已经在哈希表中存在,并且它的位置大于等于当前的左指针,那么就将左指针移动到该位置+1的位置,然后更新哈希表中nums[right]的位置为当前的right。这样可以保证窗口的有效性。 那具体的步骤应该是这样的: 初始化左指针left=0,最大长度max_len=0,创建一个哈希表来存储元素的最新索引。 然后右指针从0开始遍历数组: 对于每个nums[right],检查是否在哈希表中存在,并且其位置>=left: - 如果存在,那么移动left到该位置+1的位置。 将当前元素nums[right]的索引存入哈希表,或者更新其索引为当前的right。 计算当前窗口的长度right - left + 1,并更新max_len。 最后返回max_len。 这样的话,时间复杂度是O(n),因为每个元素最多被访问两次,左右指针各一次。空间复杂度是O(k),k为不同元素的数量,最坏情况是O(n)。 那现在要编写代码的话,比如用Python的话,可以用字典来保存元素的最新索引。初始化left=0,max_len=0。遍历右指针从0到len(nums)-1: 比如数组nums = [1,2,3,1,2,3] 当right=0,元素1,不在字典中,存入,窗口长度1,max=1。 right=1,元素2,不在,存入,窗口长度2,max=2. right=2,元素3,不在,存入,窗口长度3,max=3. right=3,元素1,此时字典中1的索引是0,但此时left=0,所以需要移动left到0+1=1。然后更新字典中的1的索引为3。窗口长度是3-1+1=3,max还是3. right=4,元素2,字典中2的索引是1,现在left=1,所以存在,移动left到1+1=2。更新2的索引为4。窗口长度4-2+1=3,max还是3. right=5,元素3,字典中的3的索引是2,此时left=2,存在,移动left到2+1=3。更新3的索引为5。窗口长度5-3+1=3,max仍为3. 最终结果是3,正确,因为最长的是[1,2,3]后面三个元素,或者中间的2,3,1等,但最长是3?不,原数组长度是6,但可能有重复,比如这里正确的应该是从索引3到5,即元素1,2,3,长度3。或者原数组是否有更长的?比如假设数组是[1,2,3,4,5],最长就是5。所以代码逻辑应该没问题。 那测试例子的话,比如: 测试案例1:输入nums = [1,2,3,1,2,3],输出是3?或者是否更长?比如从索引1到5是2,3,1,2,3?但这里重复了。可能最长的是前三个元素,或者中间某部分。比如原数组中的最长不重复子数组是3的长度? 或者另一个例子,比如nums = [2,2,2,2],最长是1。 nums = [1,2,3,4],最长是4. 那代码是否能正确处理这些情况? 再举一个例子,比如nums = [3,3,1,2,3],当right=0时,存入3的位置0。当right=1,发现3存在,位置0 >= left=0,所以left移动到1,更新字典中的3的位置为1。此时窗口长度是1-1+1=1。max=1. right=2,元素1,不在字典中,存入,窗口长度2-1+1=2,max=2. right=3,元素2,不存在,存入,窗口长度3-1+1=3,max=3. right=4,元素3,此时字典中的3的位置是1,当前left=1。存在,所以left移动到1+1=2,并更新3的位置到4。窗口长度4-2+1=3,max保持3。所以最长是3,正确吗?比如子数组是[1,2,3]长度3,或者[3,1,2]长度3。是的。 所以代码逻辑应该正确。 那现在把这个思路转化为Python代码: def longest_unique_subarray(nums): if not nums: return 0 max_len = 0 start = 0 char_map = {} for end in range(len(nums)): if nums[end] in char_map and char_map[nums[end]] >= start: start = char_map[nums[end]] + 1 char_map[nums[end]] = end current_len = end - start + 1 if current_len > max_len: max_len = current_len return max_len 那这样的代码是否正确? 比如测试nums = [1,2,3,1,2,3],调用函数返回的是3吗? 让我们逐步走一遍: 初始化start=0,max_len=0,char_map={} end=0,元素1,不在char_map,所以char_map[1]=0。current_len=0-0+1=1,max_len=1. end=1,元素2,不存在,char_map[2]=1,current_len=2,max=2. end=2,元素3,不存在,char_map[3]=2,current_len=3,max=3. end=3,元素1。此时char_map中有1的为0。检查0 >= start(0)?是的。所以start=0+1=1。char_map[1]=3。current_len=3-1+1=3。max仍为3. end=4,元素2。char_map中有2的是1。检查1 >= start=1。是的。所以start=1+1=2。char_map[2]=4. current_len=4-2+1=3. max不变。 end=5,元素3。char_map中有3的是2。start是2,检查2 >=2,是的。start=2+1=3。char_map[3]=5. current_len=5-3+1=3. 最终max是3。正确。 那这样代码是正确的。那这就是解决方案。 但需要注意,当数组为空时返回0,这在函数开头已经处理了。对于其他情况都能处理。 那这就是用户需要的解答过程。需要将思路整理清楚,分步骤解释,然后给出代码实现,并解释关键点。</think>要解决找出数组中最长不含重复数字的子数组长度问题,可以采用**滑动窗口**算法。以下是详细步骤: --- ### **方法思路** 1. **滑动窗口原理** 使用两个指针`start``end`分别表示窗口的左右边界。`end`指针不断向右扩展窗口,若遇到重复元素,则调整`start`指针到重复元素的下一个位置,确保窗口内始终无重复元素。 2. **哈希表记录位置** 通过哈希表(字典)保存每个元素**最后一次出现的位置**。当发现重复元素时,快速定位并调整窗口左边界。 3. **时间复杂度** 遍历数组一次,时间复杂度为$O(n)$,空间复杂度为$O(k)$($k$为不同元素的数量)。 --- ### **代码实现** ```python def longest_unique_subarray(nums): if not nums: return 0 max_len = 0 start = 0 char_map = {} for end in range(len(nums)): current_char = nums[end] # 如果当前字符已存在且位置在窗口内,则移动左边界 if current_char in char_map and char_map[current_char] >= start: start = char_map[current_char] + 1 # 更新字符的最新位置 char_map[current_char] = end # 计算当前窗口长度 current_len = end - start + 1 max_len = max(max_len, current_len) return max_len ``` --- ### **代码解释** 1. **初始化** - `max_len`记录最长子数组长度- `start`为窗口左边界,初始为0。 - `char_map`字典保存元素及其最后一次出现的索引。 2. **遍历数组** - 若当前元素`current_char`已在字典中,且其位置在窗口内(`>= start`),则将左边界`start`移动到重复元素的下一个位置。 - 更新字典中`current_char`的最新位置为`end`。 - 计算当前窗口长度`end - start + 1`,并更新`max_len`。 3. **边界处理** - 若输入数组为空,直接返回0。 --- ### **示例测试** ```python print(longest_unique_subarray([1, 2, 3, 1, 2, 3])) # 输出3 print(longest_unique_subarray([2, 2, 2, 2])) # 输出1 print(longest_unique_subarray([1, 2, 3, 4])) # 输出4 ``` --- ### **关键点** - **滑动窗口的动态调整**:通过哈希表快速定位重复元素,确保窗口内无重复。 - **时间复杂度优化**:单次遍历实现高效计算,避免暴力枚举的$O(n^2)$复杂度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值